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

Python:更好地理解迭代器和'join()`,python,python-internals,Python,Python Internals,join()函数接受一个iterable作为参数。然而,我想知道为什么会有: text = 'asdfqwer' 这: 明显快于: ''.join(c for c in text) 长字符串(即text*10000000)也会出现同样的情况 通过观察两个长字符串执行的内存占用情况,我认为它们都在内存中创建了一个且只有一个字符列表,然后将它们连接到一个字符串中。因此,我猜测,区别可能只在于join()如何从生成器中创建此列表,以及Python解释器在看到[c for c in text]时如何

join()
函数接受一个iterable作为参数。然而,我想知道为什么会有:

text = 'asdfqwer'
这:

明显快于:

''.join(c for c in text)
长字符串(即
text*10000000
)也会出现同样的情况


通过观察两个长字符串执行的内存占用情况,我认为它们都在内存中创建了一个且只有一个字符列表,然后将它们连接到一个字符串中。因此,我猜测,区别可能只在于
join()
如何从生成器中创建此列表,以及Python解释器在看到
[c for c in text]
时如何执行相同的操作。但是,同样,我只是猜测,所以我希望有人确认/否认我的猜测。

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

列表理解不仅仅是包装在列表中的生成器对象,因此从外部构造列表比从生成器对象创建列表更快。生成器对象针对内存效率而不是速度进行了优化


当然,字符串已经是iterable对象,所以您可以只编写
'.join(text)
。(同样,这也不如从字符串显式创建列表快。)

@AvinashRaj:您在哪里看到元组?@Matthias ya,被错误地称为元组而不是生成器。您可能指的是:有趣的是,在我的系统中,列表也比直接迭代字符串更快:
“”。join(text)
自己看看:
python-m timeit'text=“asdfqwer”*10000000;“.join([c代表文本中的c])”
(也不包括
[]
)。我通过列表获得
2.04秒/循环
,通过生成器获得
2.9秒/循环
。@DanielDarabos“它需要制作一个副本”-通过将其消耗到列表中!在这里复制我的问题注释,因为你在回答中提到了它:有趣的是,在我的系统上,
timeit
显示列表也比直接字符串迭代快。@Two-bitalchest,这是因为只有特殊情况列表和元组-其他一切(包括字符串)额外处理一次。生成器对象速度较慢。它们针对内存使用而不是迭代速度进行了优化。从现有生成器创建列表比使用列表生成器生成列表花费更多的时间。(尽管在语法上有相似之处,但列表理解不仅仅是一个包装在列表中的生成器对象。)@Andrey这不是一个很好的测试,因为您使用的是一个简短的循环函数,我们讨论的是一个必须重新处理生成器(因此必须复制)的函数。此外,在您的测试中,生成器仍然稍微慢一些(虽然不是很明显)。
''.join(c for c in text)