当前位置:  开发笔记 > 编程语言 > 正文

生成器表达式与列表理解

如何解决《生成器表达式与列表理解》经验,为你挑选了6个好方法。

什么时候应该使用生成器表达式?什么时候应该在Python中使用列表推导?

# Generator expression
(x*2 for x in range(256))

# List comprehension
[x*2 for x in range(256)]

Eli Courtwri.. 266

John的答案很好(当你想多次迭代某些事情时,列表理解会更好).但是,如果要使用任何列表方法,还应该注意使用列表.例如,以下代码将不起作用:

def gen():
    return (something for something in get_some_stuff())

print gen()[:2]     # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists

基本上,如果您所做的只是迭代一次,请使用生成器表达式.如果你想存储和使用生成的结果,那么你最好使用列表理解.

由于性能是选择其中一种的最常见原因,我的建议是不要担心它而只选择一种; 如果您发现您的程序运行速度太慢,那么只有这样您才能回过头来担心调整代码.



1> Eli Courtwri..:

John的答案很好(当你想多次迭代某些事情时,列表理解会更好).但是,如果要使用任何列表方法,还应该注意使用列表.例如,以下代码将不起作用:

def gen():
    return (something for something in get_some_stuff())

print gen()[:2]     # generators don't support indexing or slicing
print [5,6] + gen() # generators can't be added to lists

基本上,如果您所做的只是迭代一次,请使用生成器表达式.如果你想存储和使用生成的结果,那么你最好使用列表理解.

由于性能是选择其中一种的最常见原因,我的建议是不要担心它而只选择一种; 如果您发现您的程序运行速度太慢,那么只有这样您才能回过头来担心调整代码.


有时您**使用生成器 - 例如,如果您使用yield编写协同调度协程.但如果你这样做,你可能不会问这个问题;)
我知道这是旧的,但我认为值得注意的是,生成器(以及任何可迭代的)可以添加到带有扩展名的列表中:`a = [1,2,3] b = [4,5,6] a.extend( b)` - 现在将是[1,2,3,4,5,6].(你能在评论中添加新行吗?)
@jarvisteve你的例子掩盖了你所说的话.这里还有一个很好的观点.列表可以用生成器扩展,但是没有必要使它成为生成器.生成器不能使用列表进行扩展,而生成器也不是可迭代的.`a =(x表示范围(0,10)中的x),b = [1,2,3]`.`a.extend(b)`抛出异常.`b.extend(a)`将评估所有a,在这种情况下,首先使它成为一个发生器是没有意义的.
@SlaterTyranus你是100%正确的,我赞成你的准确性.尽管如此,我认为他的评论对于OP的问题是一个有用的非答案,因为它会帮助那些发现自己的人,因为他们在搜索引擎中输入了"将生成器与列表理解相结合"之类的东西.

2> dF...:

迭代生成器表达式列表理解将执行相同的操作.但是,列表推导将首先在内存中创建整个列表,而生成器表达式将动态创建项目,因此您可以将它用于非常大(也是无限!)的序列.


+1为无限.无论您对表现的关注程度如何,您都无法使用列表.
@Annan只有你已经有权访问另一个无限的发电机.例如,`itertools.count(n)`是一个无限的整数序列,从n开始,所以`(在itertools.count(n)中的项目为2**项)`将是一个无限的权力序列. 2`从'2**n`开始.
生成器在迭代后从内存中删除项目.例如,如果您只想显示大数据,那么速度很快.它不是记忆猪.发电机项目按"需要"处理.如果你想挂在列表上或再次迭代它(所以存储项目)然后使用列表理解.

3> John Milliki..:

当结果需要多次迭代或速度至关重要时,请使用列表推导.使用范围大或无限的生成器表达式.


@GuillermoAres这是"谷歌搜索"对于最重要的意义的直接结果:*比其他任何事情更重要; 最高.*
这可能会有点偏离主题,但是不幸的是“无法理解”……在这种情况下,“最重要的”是什么意思?我不是说英语的人... :)

4> tylerl..:

重要的是列表理解创建了一个新列表.生成器创建一个可迭代的对象,当您使用这些位时,它将动态地"过滤"源材料.

想象一下,你有一个名为"hugefile.txt"的2TB日志文件,你想要所有以"ENTRY"开头的行的内容和长度.

所以你尝试通过编写列表理解来开始:

logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]

这会覆盖整个文件,处理每一行,并将匹配的行存储在您的数组中.因此,该阵列最多可包含2TB的内容.这是很多内存,可能不适用于您的目的.

因此,我们可以使用生成器将"过滤器"应用于我们的内容.在我们开始迭代结果之前,实际上没有数据被读取.

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迭代器请求行(按指示过滤),迭代器又读取文件.

因此,不是以完全填充列表的形式将数据"推送"到输出函数,而是为输出函数提供一种仅在需要时"拉"数据的方法.在我们的情况下,这更有效,但不太灵活.发电机是单向的,一次通过; 我们读过的日志文件中的数据会被立即丢弃,因此我们无法返回上一行.另一方面,我们不必担心一旦完成数据就保持数据.



5> 小智..:

生成器表达式的好处是它使用更少的内存,因为它不会立即构建整个列表.当列表是中介时,最好使用生成器表达式,例如对结果求和,或者从结果中创建字典.

例如:

sum(x*2 for x in xrange(256))

dict( ((k, some_func(k) for k in some_list_of_keys) )

优点是列表没有完全生成,因此使用的内存很少(也应该更快)

但是,当所需的最终产品是列表时,您应该使用列表推导.您不会使用生成器表达式保存任何内存,因为您需要生成的列表.您还可以使用任何列表函数,如已排序或反向.

例如:

reversed( [x*2 for x in xrange(256)] )


在语言中有一个提示,即生成器表达式意味着以这种方式使用.丢了括号!`sum(x*2代表x in xrange(256))`
`sorted`和`reversed`在包含的任何可迭代的生成器表达式上都能正常工作.
这应该是`dict((k,some_func(k))for some_list_of_keys中的k).当然2.7+ dict/set理解语法甚至更甜.

6> freaker..:

从可变对象(如列表)创建生成器时,请注意在使用生成器时,生成器将在列表状态上进行评估,而不是在创建生成器时:

>>> mylist = ["a", "b", "c"]
>>> gen = (elem + "1" for elem in mylist)
>>> mylist.clear()
>>> for x in gen: print (x)
# nothing

如果您的列表有可能被修改(或该列表中的可变对象),但您需要创建生成器时的状态,则需要使用列表推导.

推荐阅读
手机用户2502851955
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有