在Python中没有列出理解
join清单:
>>> ''.join([ str(_) for _ in xrange(10) ]) '0123456789'
join
必须采取迭代。
显然, join
的参数是[ str(_) for _ in xrange(10) ]
x [ str(_) for _ in xrange(10) ]
,这是一个列表理解 。
看这个:
>>>''.join( str(_) for _ in xrange(10) ) '0123456789'
现在, str(_) for _ in xrange(10)
,no []
, join
的参数就是str(_) for _ in xrange(10)
,但结果是一样的。
为什么? str(_) for _ in xrange(10)
是否也产生一个列表或一个迭代?
>>>''.join( str(_) for _ in xrange(10) )
这被称为生成器expression式 ,在PEP 289中进行了解释。
生成器expression式与列表parsing的主要区别在于前者不会在内存中创build列表。
请注意,还有第三种方式来编写expression式:
''.join(map(str, xrange(10)))
其他受访者在回答您已经发现了一个生成器expression式 (与列表parsing相似但没有周围的方括号)时是正确的。
一般来说,genexps(因为他们是深情的知道)更有记忆效率和速度比列表理解。
但是, ''.join()
的情况下,列表理解既快速又高效。 原因在于连接需要对数据进行两次传递,所以实际上需要一个真正的列表。 如果你给它一个,它可以立即开始工作。 如果你给它一个genexp,它不能开始工作,直到它通过运行genexp来耗尽,在内存中build立一个新的列表:
~ $ python -m timeit '"".join(str(n) for n in xrange(1000))' 1000 loops, best of 3: 335 usec per loop ~ $ python -m timeit '"".join([str(n) for n in xrange(1000)])' 1000 loops, best of 3: 288 usec per loop
比较itertools.imap和map时,结果是一样的:
~ $ python -m timeit -s'from itertools import imap' '"".join(imap(str, xrange(1000)))' 1000 loops, best of 3: 220 usec per loop ~ $ python -m timeit '"".join(map(str, xrange(1000)))' 1000 loops, best of 3: 212 usec per loop
你的第二个例子使用生成器expression式而不是列表理解。 与列表理解不同的是,列表完全构build并传递给.join()
。 使用生成器expression式,项目将逐个生成并由.join()
消耗。 后者使用更less的内存,通常更快。
碰巧,列表构造函数将愉快地使用任何迭代,包括一个生成器expression式。 所以:
[str(n) for n in xrange(10)]
仅仅是“语法糖”:
list(str(n) for n in xrange(10))
换句话说,列表理解只是一个变成列表的生成器expression式。
如前所述,这是一个生成器expression式 。
从文档:
只有一个参数的调用可以省略括号。 请参阅呼叫以获取详细信息。
如果它是在parens中,而不是括号,它在技术上是一个生成器expression式。 生成器expression式首先在Python 2.4中引入。
http://wiki.python.org/moin/Generators
连接之后的部分( str(_) for _ in xrange(10) )
本身就是一个生成器expression式。 你可以做这样的事情:
mylist = (str(_) for _ in xrange(10)) ''.join(mylist)
这意味着你在上面的第二种情况下写的是完全一样的东西。
生成器有一些非常有趣的属性,不是最不重要的是,当你不需要分配整个列表时,它们不会最终分配一个完整的列表。 相反,像join这样的function将这些项目从发生器expression式中逐个“抽出”,并且在微小的中间部件上进行工作。
在你的具体例子中,列表和生成器可能不会有太大的不同,但是一般来说,我更喜欢使用生成器expression式(甚至是生成器函数),主要是因为生成器比完整列表物化。
这是一个生成器,而不是一个列表理解。 生成器也是可迭代的,但是不是首先创build整个列表,然后传递它join,它将逐个传递xrange中的每个值,这可以更有效。
第二次join
调用的参数是一个生成器expression式。 它确实产生了一个迭代。