前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python列表推导(list comprehension)VS 生成器表达式(generator expression

Python列表推导(list comprehension)VS 生成器表达式(generator expression

作者头像
银河1号
发布2019-05-16 14:27:57
1.5K0
发布2019-05-16 14:27:57
举报
文章被收录于专栏:银河系资讯

你知道以下语法之间的区别吗?

代码语言:javascript
复制
[x for x in range(5)]

(x for x in range(5))

tuple(range(5))

本文将向您介绍这里的区别。

关于列表的5个事实

首先,对列表进行简短回顾(在其他编程语言中通常称为“数组”):

列表是一种可以表示为元素集合的数据。一个简单的列表如下所示:[0, 1, 2, 3, 4, 5] 列表将所有可能类型的数据和数据组合作为其元素:

代码语言:javascript
复制
>>> a = 12
>>> b = "this is text"
>>> my_list = [0, b, ['element', 'another element'], (1, 2, 3), a]
>>> print(my_list)
[0, 'this is text', ['element', 'another element'], (1, 2, 3), 12]

列表可以编入索引。您可以使用以下语法访问任何单个元素或元素组:

代码语言:javascript
复制
>>> a = ['red', 'green', 'blue']
>>> print(a[0])
red

与字符串不同,列表在Python中是可变的。这意味着您可以替换,添加或删除元素。 您可以使用for循环和range()函数创建列表。

代码语言:javascript
复制
>>> my_list = []
>>> for x in range(10):
...     my_list.append(x * 2)
... 
>>> print(my_list)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

好的 - 那么列表推导是什么?

通常被视为Python中函数式编程的一部分,列表推导允许您使用包含较少代码的for循环创建列表。

使用列表推导来查看前一个示例的实现:

代码语言:javascript
复制
>>> comp_list = [x * 2 for x in range(10)] 
>>> print(comp_list)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

上面的示例过于简单,可以让您了解语法。使用更简单的list(range(0, 19, 2))功能可以实现相同的结果。

您还可以在推导的第一部分中使用更复杂的修改器,或添加将过滤列表的条件。像这样的东西:

代码语言:javascript
复制
>>> comp_list = [x ** 2 for x in range(7) if x % 2 == 0] 
>>> print(comp_list)
[4, 16, 36]

另一个可用选项是使用列表推导来组合多个列表并创建列表列表。乍一看,语法似乎很复杂。将列表视为外部序列和内部序列可能会有所帮助。

当您想要通过组合两个现有列表来创建列表列表时,是时候展示列表推导的强大功能了:

代码语言:javascript
复制
>>> nums = [1, 2, 3, 4, 5]
>>> letters = ['A', 'B', 'C', 'D', 'E']
>>> nums_letters = [[n, l] for n in nums for l in letters]
#the comprehensions list combines two simple lists in a complex list of lists.
>>> print(nums_letters)
>>> print(nums_letters)
[[1, 'A'], [1, 'B'], [1, 'C'], [1, 'D'], [1, 'E'], [2, 'A'], [2, 'B'], [2, 'C'], [2, 'D'], [2, 'E'], [3, 'A'], [3, 'B'], [3, 'C'], [3, 'D'], [3, 'E'], [4, 'A'], [4, 'B'], [4, 'C'], [4, 'D'], [4, 'E'], [5, 'A'], [5, 'B'], [5, 'C'], [5, 'D'], [5, 'E']]
>>>

让我们用文本尝试它,或者说字符串对象是正确的。

代码语言:javascript
复制
>>> iter_string = "some text"
>>> comp_list = [x for x in iter_string if x !=" "]
>>> print(comp_list)
['s', 'o', 'm', 'e', 't', 'e', 'x', 't']

推导不仅限于列表。您也可以创建dicts并设置推导。

代码语言:javascript
复制
>>> dict_comp = {x:chr(65+x) for x in range(1, 11)}
>>> type(dict_comp)
<class 'dict'>  
>>> print(dict_comp)
{1: 'B', 2: 'C', 3: 'D', 4: 'E', 5: 'F', 6: 'G', 7: 'H', 8: 'I', 9: 'J', 10: 'K'}

>>> set_comp = {x ** 3 for x in range(10) if x % 2 == 0}
>>> type(set_comp)
<class 'set'>  
>>> print(set_comp)
{0, 8, 64, 512, 216}

Iterable和Iterator之间的区别

如果你了解了迭代和迭代器,那么理解生成器的概念会更容易。

Iterable是数据的“序列”,您可以使用循环迭代。可迭代的最简单可见示例可以是整数列表 - [1, 2, 3, 4, 5, 6, 7]。可以迭代其他类型的数据,如字符串,dicts,元组,集合等。

基本上,任何具有iter()方法的对象都可以用作可迭代的。您可以使用hasattr()解释器中的函数进行检查。

代码语言:javascript
复制
>>> hasattr(str, '__iter__')
True  
>>> hasattr(bool, '__iter__')
False

迭代一系列数据时,就会实现迭代器协议。例如,当您使用for循环时,后台发生以下情况:

iter()在对象上调用第一个方法将其转换为迭代器对象。 在迭代器对象上调用该方法以获取序列的下一个元素。 next() 如果StopIteration没有要调用的元素,则会引发异常。

代码语言:javascript
复制
>>> simple_list = [1, 2, 3]
>>> my_iterator = iter(simple_list)
>>> print(my_iterator)
<list_iterator object at 0x7f66b6288630>  
>>> next(my_iterator)
1  
>>> next(my_iterator)
2  
>>> next(my_iterator)
3  
>>> next(my_iterator)
Traceback (most recent call last):  
  File "<stdin>", line 1, in <module>
StopIteration

生成器表达式(generator expression)

在Python中,生成器提供了一种实现迭代器协议的便捷方式。Generator是一个使用带有yield语句的函数创建的迭代。

生成器的主要特征是按需评估元素。当您使用return语句调用普通函数时,只要遇到return语句,函数就会终止。

在带有yield语句的函数中,函数的状态从上次调用中“保存”,并且可以在下次调用生成函数时被拾取

代码语言:javascript
复制
>>> def my_gen():
...     for x in range(5):
...             yield x

生成器表达式允许在没有yield关键字的情况下即时创建生成器。但是它们不能分享用yield函数创建的生成器的全部功能。

语法和概念类似于列表推导的语法和概念:

代码语言:javascript
复制
>>> gen_exp = (x ** 2 for x in range(10) if x % 2 == 0) 
>>> for x in gen_exp:
...     print(x)
0  
4  
16  
36  
64

在语法方面,唯一的区别是你使用括号而不是方括号。

列表推导和生成器表达式返回的数据类型不同。

代码语言:javascript
复制
>>> list_comp = [x ** 2 for x in range(10) if x % 2 == 0]
>>> gen_exp = (x ** 2 for x in range(10) if x % 2 == 0)
>>> print(list_comp)
[0, 4, 16, 36, 64]
>>> print(gen_exp)
<generator object <genexpr> at 0x7f600131c410>

生成器在列表中的主要优点是它占用的内存要少得多。我们可以使用sys.getsizeof()方法检查两种类型占用的内存量。

注意:在Python 2中,使用range()函数实际上无法反映大小方面的优势,因为它仍然将整个元素列表保存在内存中。但是,在Python 3中,这个例子是可行的,因为它range()返回一个范围对象。

代码语言:javascript
复制
>>> from sys import getsizeof
>>> my_comp = [x * 5 for x in range(1000)]
>>> my_gen = (x * 5 for x in range(1000))
>>> getsizeof(my_comp)
9024  
>>> getsizeof(my_gen)
88

生成器一次生成一个项目 - 因此它比列表更有内存效率。

例如,当您想迭代列表时,Python会为整个列表保留内存。生成器不会将整个序列保留在内存中,并且只会根据需要“生成”序列的下一个元素。

最后的想法

可能会吓到或劝阻新手程序员的第一件事就是教育材料的规模。这里的诀窍是将每个概念视为语言提供的选项,您不应该同时学习所有语言概念和模块。

总有不同的方法来解决同一个任务。把它作为完成工作的另一个工具。

查看英文原文:https://djangostars.com/blog/list-comprehensions-and-generator-expressions/

查看更多文章:www.apexyun.com

公众号:银河系1号

联系邮箱:public@space-explore.com

(未经同意,请勿转载)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 银河系1号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 关于列表的5个事实
  • 好的 - 那么列表推导是什么?
  • Iterable和Iterator之间的区别
  • 生成器表达式(generator expression)
  • 最后的想法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档