考虑从一个巨大的字符串中提取字母表的问题。
一种方法是
''.join([c for c in hugestring if c.isalpha()])
机制很明确:列表理解生成一个字符列表。 join方法通过访问列表的长度来了解它需要加入多少个字符。
其他方法是
''.join(c for c in hugestring if c.isalpha())
这里生成器理解导致生成器。 join方法不知道要连接多少个字符,因为生成器不具有 len 属性。所以这种连接方式应该比列表理解方法慢。
但是在python中测试表明它并不慢。为什么会这样? 任何人都可以解释加入如何在发电机上工作。
要明确:
sum(j for j in range(100))
不需要知道100,因为它可以跟踪累积总和。它可以使用生成器上的下一个方法访问下一个元素,然后添加到累积和。 但是,由于字符串是不可变的,因此累加连接字符串会在每次迭代中创建一个新字符串。所以这需要很多时间。
答案 0 :(得分:12)
当你致电str.join(gen)
,其中gen
是一个生成器时,Python会相等于list(gen)
,然后继续检查结果序列的长度。
具体来说,如果您look at the code implementing str.join
in CPython,您会看到此电话:
fseq = PySequence_Fast(seq, "can only join an iterable");
对PySequence_Fast
的调用将seq
参数转换为列表(如果它不是列表或元组)。
因此,您的两个版本的呼叫几乎完全相同。在列表理解中,您自己构建列表并将其传递到join
。在生成器表达式版本中,您传入的生成器对象在list
的开头变为join
,并且其余代码对两个版本的操作都相同。
答案 1 :(得分:1)
join()
不需要实现为序列元素的顺序附加到更长和更长的累积字符串(对于长序列来说确实非常慢);它只需要产生相同的结果。所以join()
可能只是将字符附加到某个内部内存缓冲区,并在最后创建一个字符串。另一方面,列表推导构造需要首先构建列表(通过遍历hugestring
的生成器),然后让join()
开始工作。
另外,我怀疑join()
查看列表的长度,因为它不知道每个元素都是单个字符(在大多数情况下,它不会) - 它可能只是获得一个生成器从列表中。
答案 2 :(得分:1)
至少在我的机器上,对于我测试的情况,列表理解速度更快,可能是由于''.join
能够优化内存分配。它可能仅取决于您正在测试的具体示例(例如,如果您正在测试的条件发生频率较低,则CPython提前支付的不知道长度的价格可能会更小):
In [18]: s = ''.join(np.random.choice(list(string.printable), 1000000))
In [19]: %timeit ''.join(c for c in s if c.isalpha())
10 loops, best of 3: 69.1 ms per loop
In [20]: %timeit ''.join([c for c in s if c.isalpha()])
10 loops, best of 3: 61.8 ms per loop