在Python中什么时候应该使用生成器表达式,什么时候应该使用列表推导式?
# Generator expression
(x*2 for x in range(256))
# List comprehension
[x*2 for x in range(256)]
在Python中什么时候应该使用生成器表达式,什么时候应该使用列表推导式?
# Generator expression
(x*2 for x in range(256))
# List comprehension
[x*2 for x in range(256)]
当前回答
对于函数式编程,我们希望使用尽可能少的索引。因此,如果我们想在获取元素的第一个切片后继续使用元素,islice()是一个更好的选择,因为迭代器状态会被保存。
from itertools import islice
def slice_and_continue(sequence):
ret = []
seq_i = iter(sequence) #create an iterator from the list
seq_slice = islice(seq_i,3) #take first 3 elements and print
for x in seq_slice: print(x),
for x in seq_i: print(x**2), #square the rest of the numbers
slice_and_continue([1,2,3,4,5])
输出:1 2 3 16 25
其他回答
我正在使用Hadoop Mincemeat模块。我认为这是一个值得注意的好例子:
import mincemeat
def mapfn(k,v):
for w in v:
yield 'sum',w
#yield 'count',1
def reducefn(k,v):
r1=sum(v)
r2=len(v)
print r2
m=r1/r2
std=0
for i in range(r2):
std+=pow(abs(v[i]-m),2)
res=pow((std/r2),0.5)
return r1,r2,res
在这里,生成器从一个文本文件(最大15GB)中获取数字,并使用Hadoop的map-reduce对这些数字应用简单的数学运算。如果我没有使用yield函数,而是使用一个列表理解,那么计算总和和平均值将花费更长的时间(更不用说空间复杂性了)。
Hadoop是一个很好的例子,可以使用生成器的所有优点。
当结果需要多次迭代时,或者在速度非常重要的情况下,使用列表推导式。在范围较大或无穷大的地方使用生成器表达式。
有关更多信息,请参阅生成器表达式和列表推导式。
对于函数式编程,我们希望使用尽可能少的索引。因此,如果我们想在获取元素的第一个切片后继续使用元素,islice()是一个更好的选择,因为迭代器状态会被保存。
from itertools import islice
def slice_and_continue(sequence):
ret = []
seq_i = iter(sequence) #create an iterator from the list
seq_slice = islice(seq_i,3) #take first 3 elements and print
for x in seq_slice: print(x),
for x in seq_i: print(x**2), #square the rest of the numbers
slice_and_continue([1,2,3,4,5])
输出:1 2 3 16 25
生成器表达式的好处是它使用更少的内存,因为它不会一次构建整个列表。生成器表达式最好在列表作为中介时使用,例如对结果求和,或从结果中创建字典。
例如:
sum(x*2 for x in xrange(256))
dict( (k, some_func(k)) for k in some_list_of_keys )
这样做的好处是列表不是完全生成的,因此占用的内存很少(而且应该更快)。
但是,当期望的最终产品是一个列表时,应该使用列表推导式。使用生成器表达式不会节省任何内存,因为您需要生成的列表。您还可以使用任何列表函数,如sorted或reversed。
例如:
reversed( [x*2 for x in xrange(256)] )
重要的一点是,列表推导式创建了一个新列表。生成器创建一个可迭代对象,该对象将在您使用比特时实时“过滤”源材料。
假设您有一个名为“hugefile.txt”的2TB日志文件,您需要以单词“ENTRY”开头的所有行的内容和长度。
所以你可以试着从写一个列表理解开始:
logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]
这将获取整个文件,处理每一行,并将匹配的行存储在数组中。因此,这个数组最多可以包含2TB的内容。这是一个很大的RAM,可能不适合您的目的。
因此,我们可以使用生成器对内容应用“过滤器”。直到我们开始对结果进行迭代,才实际读取数据。
logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))
我们的文件连一行都没读过。事实上,假设我们想进一步过滤结果:
long_entries = ((line,length) for (line,length) in entry_lines if length > 80)
仍然没有读取任何数据,但是现在我们已经指定了两个生成器,它们将按照我们的意愿处理数据。
让我们把过滤后的行写入另一个文件:
outfile = open("filtered.txt","a")
for entry,length in long_entries:
outfile.write(entry)
现在我们读取输入文件。当我们的for循环继续请求额外的行时,long_entries生成器要求来自entry_lines生成器的行,只返回长度大于80个字符的行。然后,entry_lines生成器从logfile迭代器请求行(按指示过滤),然后logfile迭代器读取文件。
因此,您不是以完全填充的列表的形式将数据“推”到输出函数中,而是为输出函数提供了一种仅在需要时“拉”数据的方法。在我们的例子中,这更有效,但不那么灵活。生成器是单向的,一遍;我们读取的日志文件中的数据立即被丢弃,因此我们不能返回到前一行。另一方面,一旦我们处理完数据,我们就不必担心如何保存数据。