【问题标题】:How do I append one string to another in Python?如何在 Python 中将一个字符串附加到另一个字符串?
【发布时间】:2011-05-25 00:36:10
【问题描述】:

我想要一种在 Python 中将一个字符串附加到另一个字符串的有效方法,除了以下内容。

var1 = "foo"
var2 = "bar"
var3 = var1 + var2

有什么好的内置方法可以使用吗?

【问题讨论】:

  • TL;DR: 如果您只是在寻找附加字符串的简单方法,而不关心效率:"foo" + "bar" + str(3)

标签: python string append


【解决方案1】:

使用 add 函数附加字符串:

str1 = "Hello"
str2 = " World"
str3 = str1.__add__(str2)
print(str3)

输出:

Hello World

【讨论】:

  • str + str2 仍然更短。
【解决方案2】:

Python 3.6 为我们提供了 f-strings,这是一种享受:

var1 = "foo"
var2 = "bar"
var3 = f"{var1}{var2}"
print(var3)                       # prints foobar

花括号内几乎可以做任何事情

print(f"1 + 1 == {1 + 1}")        # prints 1 + 1 == 2

【讨论】:

    【解决方案3】:

    如果你需要做很多追加操作来构建一个大字符串,你可以使用StringIO或cStringIO。界面就像一个文件。即:您 write 将文本附加到它。

    如果您只是附加两个字符串,那么只需使用 +

    【讨论】:

      【解决方案4】:

      基本上没有区别。唯一一致的趋势是每个版本的 Python 似乎都变得越来越慢...... :(


      列表

      %%timeit
      x = []
      for i in range(100000000):  # xrange on Python 2.7
          x.append('a')
      x = ''.join(x)
      

      Python 2.7

      1 次循环,3 次取胜:7.34 秒/次

      Python 3.4

      1 次循环,3 次取胜:7.99 秒/次

      Python 3.5

      1 次循环,3 次取胜:8.48 秒/次

      Python 3.6

      1 次循环,3 次取胜:9.93 秒/次


      字符串

      %%timeit
      x = ''
      for i in range(100000000):  # xrange on Python 2.7
          x += 'a'
      

      Python 2.7

      1 个循环,3 次取胜:7.41 秒每个循环

      Python 3.4

      1 次循环,3 次取胜:9.08 秒/次

      Python 3.5

      1 次循环,3 次取胜:8.82 秒/次

      Python 3.6

      1 次循环,3 次取胜:每次循环 9.24

      【讨论】:

      • 我想这取决于。我在 Python2.7 上分别得到 1.19 s992 ms
      【解决方案5】:

      如果您只有一个对字符串的引用,并且您将另一个字符串连接到末尾,CPython 现在会处理这种特殊情况并尝试就地扩展字符串。

      最终结果是操作摊销O(n)。

      例如

      s = ""
      for i in range(n):
          s+=str(i)
      

      以前是O(n^2),现在是O(n)。

      来自源(bytesobject.c):

      void
      PyBytes_ConcatAndDel(register PyObject **pv, register PyObject *w)
      {
          PyBytes_Concat(pv, w);
          Py_XDECREF(w);
      }
      
      
      /* The following function breaks the notion that strings are immutable:
         it changes the size of a string.  We get away with this only if there
         is only one module referencing the object.  You can also think of it
         as creating a new string object and destroying the old one, only
         more efficiently.  In any case, don't use this if the string may
         already be known to some other part of the code...
         Note that if there's not enough memory to resize the string, the original
         string object at *pv is deallocated, *pv is set to NULL, an "out of
         memory" exception is set, and -1 is returned.  Else (on success) 0 is
         returned, and the value in *pv may or may not be the same as on input.
         As always, an extra byte is allocated for a trailing \0 byte (newsize
         does *not* include that), and a trailing \0 byte is stored.
      */
      
      int
      _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
      {
          register PyObject *v;
          register PyBytesObject *sv;
          v = *pv;
          if (!PyBytes_Check(v) || Py_REFCNT(v) != 1 || newsize < 0) {
              *pv = 0;
              Py_DECREF(v);
              PyErr_BadInternalCall();
              return -1;
          }
          /* XXX UNREF/NEWREF interface should be more symmetrical */
          _Py_DEC_REFTOTAL;
          _Py_ForgetReference(v);
          *pv = (PyObject *)
              PyObject_REALLOC((char *)v, PyBytesObject_SIZE + newsize);
          if (*pv == NULL) {
              PyObject_Del(v);
              PyErr_NoMemory();
              return -1;
          }
          _Py_NewReference(*pv);
          sv = (PyBytesObject *) *pv;
          Py_SIZE(sv) = newsize;
          sv->ob_sval[newsize] = '\0';
          sv->ob_shash = -1;          /* invalidate cached hash value */
          return 0;
      }
      

      凭经验验证很容易。

      $ python -m timeit -s"s=''" "for i in xrange(10):s+='a'" 1000000 次循环,3 次中的最佳:每个循环 1.85 微秒 $ python -m timeit -s"s=''" "for i in xrange(100):s+='a'" 10000 次循环,3 次中的最佳:每个循环 16.8 微秒 $ python -m timeit -s"s=''" "for i in xrange(1000):s+='a'" 10000 个循环,3 个中最好的:每个循环 158 微秒 $ python -m timeit -s"s=''" "for i in xrange(10000):s+='a'" 1000 个循环,3 个循环中的最佳:每个循环 1.71 毫秒 $ python -m timeit -s"s=''" "for i in xrange(100000):s+='a'" 10 个循环,3 个循环中的最佳:每个循环 14.6 毫秒 $ python -m timeit -s"s=''" "for i in xrange(1000000):s+='a'" 10 个循环,3 个循环中的最佳:每个循环 173 毫秒

      重要的是但请注意,此优化不是 Python 规范的一部分。据我所知,它仅在 cPython 实现中。例如,对 pypy 或 jython 的相同经验测试可能会显示较旧的 O(n**2) 性能。

      $ pypy -m timeit -s"s=''" "for i in xrange(10):s+='a'" 10000 个循环,最好的 3 个:每个循环 90.8 微秒 $ pypy -m timeit -s"s=''" "for i in xrange(100):s+='a'" 1000 个循环,最好的 3 个:每个循环 896 微秒 $ pypy -m timeit -s"s=''" "for i in xrange(1000):s+='a'" 100 个循环,3 个循环中的最佳:每个循环 9.03 毫秒 $ pypy -m timeit -s"s=''" "for i in xrange(10000):s+='a'" 10 个循环,3 个循环中的最佳:每个循环 89.5 毫秒

      到目前为止一切都很好,但是,

      $ pypy -m timeit -s"s=''" "for i in xrange(100000):s+='a'" 10 个循环,3 个循环中的最佳:每个循环 12.8 秒

      哎哟比二次方还要糟糕。所以 pypy 对短字符串表现良好,但对大字符串表现不佳。

      【讨论】:

      【解决方案6】:

      这真的取决于您的应用程序。如果您要遍历数百个单词并希望将它们全部附加到一个列表中,.join() 会更好。但如果你要写一个长句子,你最好使用+=

      【讨论】:

        【解决方案7】:
        a='foo'
        b='baaz'
        
        a.__add__(b)
        
        out: 'foobaaz'
        

        【讨论】:

        • 代码很好,但附带解释会有所帮助。为什么使用此方法而不是此页面上的其他答案?
        • 使用a.__add__(b) 等同于编写a+b。当您使用+ 运算符连接字符串时,Python 将在左侧字符串上调用__add__ 方法,并将右侧字符串作为参数传递。
        【解决方案8】:

        不要。

        也就是说,在大多数情况下,最好一次性生成整个字符串,而不是附加到现有字符串。

        例如,不要这样做:obj1.name + ":" + str(obj1.count)

        改为:使用"%s:%d" % (obj1.name, obj1.count)

        这样会更容易阅读,效率更高。

        【讨论】:

        • 很抱歉,没有什么比第一个示例(字符串 + 字符串)更容易阅读的了,第二个示例可能更高效,但可读性并不高
        • @ExceptionSlayer, string + string 很容易理解。但是"&lt;div class='" + className + "' id='" + generateUniqueId() + "'&gt;" + message_text + "&lt;/div&gt;",我发现比"&lt;div class='{classname}' id='{id}'&gt;{message_text}&lt;/div&gt;".format(classname=class_name, message_text=message_text, id=generateUniqueId())更易读且更容易出错
        • 当我尝试做的是大致相当于 PHP/perl 的“string .= verifydata()”或类似的东西时,这根本没有帮助。
        • 在这种情况下,该问题的答案是“不,因为该方法不涵盖我的用例”
        • 使用 Python 3.6 我们有 f"&lt;div class='{class_name}' id='{generateUniqueId()}'&gt;{message_text}&lt;/div&gt;"
        【解决方案9】:

        不要过早优化。如果您没有理由相信字符串连接会导致速度瓶颈,那么请坚持使用++=

        s  = 'foo'
        s += 'bar'
        s += 'baz'
        

        也就是说,如果您的目标是 Java 的 StringBuilder,规范的 Python 习惯用法是将项目添加到列表中,然后使用 str.join 将它们全部连接到最后:

        l = []
        l.append('foo')
        l.append('bar')
        l.append('baz')
        
        s = ''.join(l)
        

        【讨论】:

        • 我不知道将字符串构建为列表然后 .join() 对速度的影响是什么,但我发现它通常是最干净的方式。在我编写的 SQL 模板引擎的字符串中使用 %s 表示法方面,我也取得了巨大的成功。
        • @Richo 使用 .join 效率更高。原因是 Python 字符串是不可变的,所以重复使用 s += more 会分配大量连续更大的字符串。 .join 将从其组成部分一次性生成最终字符串。
        • @Ben,这方面已经有了显着的改进——看我的回答
        【解决方案10】:
        str1 = "Hello"
        str2 = "World"
        newstr = " ".join((str1, str2))
        

        用空格作为分隔符将 str1 和 str2 连接起来。你也可以"".join(str1, str2, ...)str.join() 采用可迭代对象,因此您必须将字符串放入列表或元组中。

        这与内置方法的效率差不多。

        【讨论】:

        • 如果 str1 为空会发生什么?会设置空格吗?
        • @JürgenK。是的。它不会以不同的方式处理空字符串。它只需要所有字符串并将sperator放在两者之间。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-06
        • 1970-01-01
        • 2020-04-13
        相关资源
        最近更新 更多