Python:更好地理解迭代器和`join()`

Peq*_*que 10 python python-internals

join()函数接受iterable作为参数.但是,我想知道为什么:

text = 'asdfqwer'
Run Code Online (Sandbox Code Playgroud)

这个:

''.join([c for c in text])
Run Code Online (Sandbox Code Playgroud)

明显快于:

''.join(c for c in text)
Run Code Online (Sandbox Code Playgroud)

长字符串也是如此(即text * 10000000).

使用长字符串观察两个执行的内存占用,我认为它们都在内存中创建一个且只有一个字符列表,然后将它们连接成一个字符串.所以我猜也许区别仅在于如何join()从生成器创建这个列表以及Python解释器在看到它时如何做同样的事情[c for c in text].但是,我只是在猜测,所以我希望有人确认/否认我的猜测.

che*_*ner 10

join方法读取其输入两次; 一次确定为结果字符串对象分配多少内存,然后再次执行实际连接.传递列表比传递它需要复制的生成器对象更快,因此它可以迭代两次.

列表推导不仅仅是包含在列表中的生成器对象,因此从外部构建列表比join从生成器对象创建列表更快.生成器对象针对内存效率而非速度进行了优化.

当然,字符串已经是一个可迭代的对象,所以你可以写''.join(text).(同样,这也不如从字符串中显式创建列表那么快.)

  • 因为你在答案中提到它,所以在这里再现我的问题评论:有趣的是,我的系统上的`timeit`表明列表比这里的直接字符串迭代更快. (2认同)
  • @ Two-BitAlchemist,因为[`PySequence_Fast`](https://hg.python.org/cpython/file/tip/Objects/abstract.c#l1768)只有特例列表和元组 - 其他一切(包括字符串)处理一次额外的时间. (2认同)