python算术表达式生成列表_Python 列表推导和生成器表达式

列表推导和生成器表达式

列表推导是构建列表的一种方式,生成器表达式可以用来创建其他任何类型的序列。

列表推导

列表,可以通过多种方式构建:

. 使用一对方括号表示空列表:[]

. 使用方括号,里面的项用逗号隔开:[a],[a, b, c]

. 使用类型构造器:list() 或者 list(iterable)

. 使用列表推导:[x for x in iterable]

以上都是生成列表的形式。其中,列表推导更具有可读性。

列表推导和可读性

通过下面两个示例,对比使用方括号和列表推导:

示例 1:将字符串变为 Unicode 码位的列表

>>> letters = "abcdefg"

>>> codes = []

>>> for letter in letters:

... codes.append(ord(letter))

...

>>> codes

[97, 98, 99, 100, 101, 102, 103]

示例 2:将字符串变为 Unicode 码位的另一种写法

>>> letters = "abcdefg"

>>> codes = [ord(letter) for letter in letters]

>>> codes

[97, 98, 99, 100, 101, 102, 103]

两者想比较下,列表推导更具有可读性。当然,列表推导乱用也会有副作用。通常的原则是,用列表推导创建新列表的时候,尽可能简短。若列表推导超过两行,可考虑使用 for 循环创建。但并没有硬性要求,选择权在使用者手上。

变量泄露

在 Python 2 中,列表推导存在变量泄露问题。但在 Python 3 中,这个问题将不会再出现。

Python 2 列表推导示例:

Python 2.7.12 (default, Oct 8 2019, 14:14:10)

[GCC 5.4.0 20160609] on linux2

Type "help", "copyright", "credits" or "license" for more information.

>>> x = "this word"

>>> dummy = [x for x in "abc"]

>>> x

'c'

从结果可见,x 的值被取代了。

这是 Python 3 中的代码:

Python 3.6.5 |Anaconda, Inc.| (default, Apr 29 2018, 16:14:56)

[GCC 7.2.0] on linux

Type "help", "copyright", "credits" or "license" for more information.

>>> x = "this word"

>>> dummy = [x for x in "abc"]

>>> x

'this word'

>>> dummy

['a', 'b', 'c']

x 的值没有发生改变,dummy 也创建成功。

过滤元素

这里尝试同 filter() 和 map() 结合比较。

使用列表推导和 map/filter 结合创建同样的列表

>>> letters = "abcdefg"

>>> match_portion = [ord(letter) for letter in letters if ord(letter) > 100]

>>> match_portion

[101, 102, 103]

>>> match_portion = list(filter(lambda x: x > 100, map(ord, letters)))

>>> match_portion

[101, 102, 103]

这是两者各自的实现方式

In [4]: %timeit [ord(letter) for letter in letters if ord(letter) > 100]

764 ns ± 4.36 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [5]: %timeit list(filter(lambda x: x > 100, map(ord, letters)))

1.37 µs ± 5.37 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

这里是尝试比较两者的运行速度,可以看出, map/filter 组合比列表推导要慢些。

map() 函数接受两个参数,一个是函数,一个是 Iterable,map 将传入的函数依次作用到序列的每个元素,并把结果作为新的 Iterator 返回。

filter() 函数也接受一个函数和一个序列。和 map() 不同,filter() 把传入的函数依次作用于每个元素,然后依据返回值是 True 还是 False 决定保留还是丢弃该元素。

map 和 filter 更详细的讨论,会在后面另开篇幅进行阐述。

生成器表达式

虽然列表推导能够用来初始化元组,数组或者其他序列类型,但是生成器表达式是更好的选择。因为生成器表达式遵守迭代器协议,是逐个生产出元素,而不是创建一个完整的列表,然后把列表传递到某个构造函数中。两者比较,显然是生成器表达式更节约内存。

生成器表达式的语法与列表推导类似,只是将方括号换成圆括号。

示例用生成器表达式初始化元组

>>> letters = "abcdefg"

>>> tuple(ord(letter) for letter in letters)

(97, 98, 99, 100, 101, 102, 103)

注意:如果生成器表达式是一个函数调用过程中的唯一参数,那么不需要额外在用括号将它们围起来。

>>> tuple(ord(letter) for letter in letters)

(97, 98, 99, 100, 101, 102, 103)

>>> tuple((ord(letter) for letter in letters))

(97, 98, 99, 100, 101, 102, 103)

效果相同,若生成器表达式是唯一参数,不需要额外括号。

尝试使用生成器表达式实现笛卡尔积,示例如下:

>>> letters = ['a', 'b', 'c', 'd', 'e', 'f']

>>> nums = ['1', '2', '3']

>>> for le_num in ('{} {}'.format(le, num) for le in letters for num in nums):

... print(le_num)

...

a 1

a 2

a 3

b 1

b 2

b 3

c 1

c 2

...

在这里,生成器表达式会在每次 for 循环运行的时候才产生一个组合,所以内存不会留下组合的列表。若两个列表元素数量非常多,生成器表达式在这里就能大大减少 for 运行的开销。

此处只是简单介绍生成器如何初始化列表外的序列,以及避免额外的内存占用。详细的生成器工作原理,后续同样会另开篇幅进行阐述。

参考资料

来源

David M. Beazley;Brian K. Jones.Python Cookbook, 3rd Edtioni.O’Reilly Media.2013.

“4. Built-in Types”.docs.python.org.Retrieved 14 January 2020

廖雪峰.“Python 教程”.liaoxuefeng.com.[2020-01-17].

欢迎关注微信公众号《书所集录》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值