Python 连接字符串。生成器还是列表理解?

Python 连接字符串。生成器还是列表理解?,python,Python,考虑从一个巨大的字符串中提取字母的问题 一种方法是 ''.join([c for c in hugestring if c.isalpha()]) ''.join(c for c in hugestring if c.isalpha()) 机制很清楚:列表理解生成一个字符列表。join方法通过访问列表的长度知道需要连接多少个字符 另一种方法是 ''.join([c for c in hugestring if c.isalpha()]) ''.join(c for c in hugestr

考虑从一个巨大的字符串中提取字母的问题

一种方法是

''.join([c for c in hugestring if c.isalpha()])
''.join(c for c in hugestring if c.isalpha())
机制很清楚:列表理解生成一个字符列表。join方法通过访问列表的长度知道需要连接多少个字符

另一种方法是

''.join([c for c in hugestring if c.isalpha()])
''.join(c for c in hugestring if c.isalpha())
在这里,生成器理解将生成一个生成器。联接方法不知道要联接多少个字符,因为生成器不具有len属性。因此,这种连接方法应该比列表理解方法慢

但是在python中的测试表明它并不慢。为什么会这样? 有人能解释一下join是如何在发电机上工作的吗

要明确的是:

sum(j for j in range(100))
不需要知道100,因为它可以跟踪累计总和。它可以使用生成器上的next方法访问下一个元素,然后将其添加到累积和中。 但是,由于字符串是不可变的,因此累积地连接字符串将在每次迭代中创建一个新字符串。因此,这将花费大量时间。

join()
不需要将序列的元素顺序追加到一个越来越长的累积字符串中(对于长序列来说,这确实非常慢);它只需要产生相同的结果。因此,
join()
可能只是将字符附加到某个内部内存缓冲区,并在结尾处从中创建一个字符串。另一方面,列表理解构造需要首先构造列表(通过遍历
hugestring
的生成器),然后让
join()
开始工作


另外,我怀疑
join()
是否会查看列表的长度,因为它不知道每个元素都是一个字符(在大多数情况下,它不是)-它可能只是从列表中获得一个生成器。

至少在我的机器上,对于我测试的情况,列表理解更快,可能是由于
''。加入能够优化内存分配的
。这可能仅仅取决于您正在测试的特定示例(例如,如果您正在测试的条件发生的频率较低,那么CPython为不提前知道长度而支付的价格可能会更低):


当您调用
str.join(gen)
其中
gen
是一个生成器时,Python会在继续检查结果序列的长度之前执行与
list(gen)
等效的操作

具体来说,如果您愿意,您将看到以下呼叫:

    fseq = PySequence_Fast(seq, "can only join an iterable");
调用
PySequence\u Fast
seq
参数转换为列表(如果它不是列表或元组)


因此,两个版本的呼叫处理方式几乎相同。在列表理解中,您自己构建列表并将其传递到
join
。在generator expression版本中,您传入的generator对象在
join
的开头变成了一个
列表,其余的代码对这两个版本的操作相同。

引用解释器C层代码为此使用了一个完整的(但私有的)
\PyUnicodeWriter
API(和其他类似的“逐段构建字符串”情况)。与Java的
StringBuilder
类相比。这就是说,@Blckknight看起来是正确的;如果它还不是
列表
元组
,它会在内部将输入转换为
列表
。它还看起来会执行预计算过程来计算最终值的长度,以便按照这是列表理解被超优化的产物(它们直接构建
列表
,其中生成器表达式只
生成必须使用通用迭代器协议使用的值),而不是任何特定于
''如何工作的内容。join
工作。运行相同的测试,但将
''替换为
list
(在第二种情况下,如果它是多余的,则可以完全忽略它)。生成器表达式周围的
列表
构造函数速度明显较慢,对于如此大的输入,它显然与
列表
相关的查找或函数调用成本无关。因此速度操作通知的差异应该纯粹是间接的,对吧?@Ev.Kounis:提问者说这两个版本是不同的速度相似(“不慢”),如果他们同时测量
join
和列表理解所花费的时间,这是有意义的。如果只测量
join
,则生成器表达式版本将较慢,因为在进行任何字符串连接之前,它必须将整个生成器转储到列表中。这将花费大约相同的时间在另一个版本中,我正在构建列表理解。