【问题标题】:String concatenation with + vs. f-string使用 + 与 f-string 进行字符串连接
【发布时间】:2020-03-29 12:35:35
【问题描述】:

假设我有两个变量:

>>> a = "hello"
>>> b = "world"

我可以通过两种方式连接它们;使用+:

>>> a + b
"helloworld"

或者使用 f 字符串:

>>> f"{a}{b}"
"helloworld"

哪种方式更好或更好的做法?有人告诉我 f-string 在性能和鲁棒性方面是更好的做法,我想详细了解原因。

【问题讨论】:

  • 意见:方法一更容易理解,与其他一些语言的方法类似。方法 2 适用于格式化字符串,需要将固定文本和变量结合起来。
  • f'{a}{b}{c}'a + b + c 更有效,因为您从三个现有字符串创建一个新字符串,而不是从ab 中创建一个字符串,从a+b 中创建另一个字符串和c。对于仅连接两个字符串,请使用在给定上下文中更具可读性的那个。

标签: python python-3.x string performance f-string


【解决方案1】:

如果你有更多的字符串(>2 个,每个 1 个字符)要连接,f-strings 表现更好:

>>> from timeit import timeit

>>> timeit('a+b', setup='a,b = "h", "e"')
0.05678774899979544
>>> timeit('f"{a}{b}"', setup='a,b = "h", "e"')
0.09656870200024059

>>> timeit('a+b+c', setup='a,b,c = "h", "e", "l"')
0.09475198700010878
>>> timeit('f"{a}{b}{c}"', setup='a,b,c = "h", "e", "l"')
0.08498188300018228

>>> timeit('a+b+c+d', setup='a,b,c,d = "h", "e", "l", "l"')
0.13406166100003247
>>> timeit('f"{a}{b}{c}{d}"', setup='a,b,c,d = "h", "e", "l", "l"')
0.09481844199990519

>>> timeit('a+b+c+d+e', setup='a,b,c,d,e = "h", "e", "l", "l","o"')
0.21804361799991057
>>> timeit('f"{a}{b}{c}{d}{e}"', setup='a,b,c,d,e = "h", "e", "l", "l","o"')
0.11850353900013033

【讨论】:

    【解决方案2】:

    在性能方面,我原以为格式字符串文字比字符串连接要快得多,但我震惊地发现事实并非如此。

    我使用timeit 模块来测试格式化字符串文字与字符串连接所花费的时间。我测试了长度为 10 到 100 万个字符的字符串。

    from timeit import timeit
    import matplotlib.pyplot as plt
    n = 1000000000
    
    setup = """\
    a = 'a'*{str_len}
    b = 'b'*{str_len}
    """
    
    fstr_stmt = """\
    f'{a}{b}'
    """
    
    concat_stmt = """\
    a+b
    """
    
    str_lens = [10, 100, 1000, 10000, 100000, 1000000]
    fstr_t = []
    concat_t = []
    for str_len in str_lens:
        n_iters = n//str_len
        fstr_t.append(timeit(setup=setup.format(str_len=str_len), stmt=fstr_stmt, number=n_iters)/n_iters)
        concat_t.append(timeit(setup=setup.format(str_len=str_len), stmt=concat_stmt, number=n_iters)/n_iters)
        ratio = fstr_t[-1]/concat_t[-1]
        print(f"For two strings of length {str_len:7d}, concatenation is {ratio:.5f} times faster than f-strings")
    plt.plot(str_lens, fstr_t, "r*-")
    plt.plot(str_lens, concat_t, "c*-")
    plt.xscale("log")
    plt.yscale("log")
    plt.xlabel("String length (log scale)")
    plt.ylabel("Seconds per iteration (log scale)")
    plt.grid()
    plt.show()
    

    控制台输出:

    For two strings of length      10, concatenation is 1.06938 times faster than f-strings
    For two strings of length     100, concatenation is 1.14887 times faster than f-strings
    For two strings of length    1000, concatenation is 1.13994 times faster than f-strings
    For two strings of length   10000, concatenation is 1.26934 times faster than f-strings
    For two strings of length  100000, concatenation is 1.21585 times faster than f-strings
    For two strings of length 1000000, concatenation is 1.01816 times faster than f-strings
    

    还有剧情:

    总结: 使用字符串连接运算符比使用格式字符串文字稍快。除非您正在执行数十万个字符串连接并且需要非常快速地完成它们,否则选择的实现不太可能产生影响。

    从可读性的角度来看,f 字符串文字比字符串连接更美观且更易于阅读。此外,正如 Daniel 的回答所指出的,f-strings 能够处理不同类型的输入,而使用 + 需要两个对象都是字符串(或 __add____radd__ 方法的重载)。

    编辑:正如 chepner 在他们的评论中指出的那样,当涉及两个以上的字符串时,使用 f 字符串 更有效。例如,将另一个变量 c 添加到 setup 和 timeit 语句会产生以下控制台输出:

    For three strings of length      10, concatenation is 0.77931 times faster than f-strings
    For three strings of length     100, concatenation is 0.67699 times faster than f-strings
    For three strings of length    1000, concatenation is 0.60220 times faster than f-strings
    For three strings of length   10000, concatenation is 1.27484 times faster than f-strings
    For three strings of length  100000, concatenation is 0.98911 times faster than f-strings
    For three strings of length 1000000, concatenation is 0.60201 times faster than f-strings
    

    【讨论】:

      【解决方案3】:

      方法 2 是一种更清晰的方式来表达您希望将字符串连接在一起。这就是为什么我建议在您提供的情况下使用该方法。

      方法一的问题是当你开始使用不同的类型时。

      # integers
      a = 1
      b = 2
      a + b # 3
      f'{a}{b}' # 12
      
      # lists
      a = [2, 3]
      b = [5, 6]
      a + b  # [2, 3, 5, 6]
      f'{a}{b}' # [2, 3][5, 6]
      
      # dicts
      a = {'a': 3}
      b = {'a': 5}
      a + b # TypeError
      f'{a}{b}' # {'a': 3}{'b': 5}
      

      当值不是字符串时,您会得到不同的结果。我无法谈论性能,但根据该行执行的次数,它几乎没有区别。

      【讨论】:

        【解决方案4】:

        这有两个方面:性能和便利性。

        在 Python 3.8.0 中使用 timeit,我发现使用 f 字符串的连接始终比 ​​+ 慢,但对于较长的字符串,百分比差异很小:

        >>> from timeit import timeit
        >>> timeit('a + b', setup='a, b = "hello", "world"')
        0.059246900000289315
        >>> timeit('f"{a}{b}"', setup='a, b = "hello", "world"')
        0.06997206999949412
        >>> timeit('a + b', setup='a, b = "hello"*100, "world"*100')
        0.10218418099975679
        >>> timeit('f"{a}{b}"', setup='a, b = "hello"*100, "world"*100')
        0.1108272269993904
        >>> timeit('a + b', setup='a, b = "hello"*10000, "world"*10000')
        2.6094200410007033
        >>> timeit('f"{a}{b}"', setup='a, b = "hello"*10000, "world"*10000')
        2.7300010479993944
        

        但是,当您的输入还不是字符串时,f-string 可能会更方便一些:

        >>> a, b = [1, 2, 3], True
        >>> str(a) + str(b)
        '[1, 2, 3]True'
        >>> f'{a}{b}'
        '[1, 2, 3]True'
        

        【讨论】:

          猜你喜欢
          • 2012-07-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-09
          • 1970-01-01
          • 2023-03-02
          • 2011-12-23
          相关资源
          最近更新 更多