Python中的列表生成式(List Comprehensions)和列表生成器(Generator)

Python中的列表生成式(List Comprehensions)和列表生成器(Generator),是Python提供的两个高级应用机制。

生成式是一种简写机制,坚持了龟叔的“Python要简单优雅”的设计理念。

生成器虽然翻译成中文以后只有一字之差,但是实现的机制和生成式已经完全不同了。它存储的是一个算法,而非具体数据。如何理解呢?听我娓娓道来。

一、列表生成式(List Comprehensions)

列表生成式是Python提供的一种简易的列表生成表达式。对于一些极其规律且简单逻辑的列表生成算法,可以用列表生成式一行搞定。

1.1语法格式:

它的语法格式是:

[列表元素模式(空格)列表元素生成算法(数据来源)表达]

举个例子:

 有如下需求:要生成[1×1, 2×2, 3×3, …, 10×10]这样的一个list

传统的思路是:

L = []
for x in range(1, 11):
    L.append(x * x)

接下来是列表生成式写法:

[x * x for x in range(1, 11)]

是不是简洁了很多?我想,不用我解释,应该也浅显易懂吧。

如果翻译成白话文,上面这条语句的意思是:

生成这样一个列表,他的每一个元素都是x*x,其中,x的值是这样的 for x in range(1, 11)

1.2列表生成式中的循环和选择

列表生成式中的for循环后可加if判断

例如:求偶数平方数列。可以用列表生成式来书写

[x * x for x in range(1, 11) if x % 2 == 0]

.例如:生成’ABC‘和’XYZ’的完全结合子串

[m + n for m in 'ABC' for n in 'XYZ']

1.3列表生成式的元素模式花式表达

列表生成式也可以使用两个变量来生成list

d = {'x': 'A', 'y': 'B', 'z': 'C' }
[k + '=' + v for k, v in d.items()]

.列表中所有字符串变小写

L = ['Hello', 'World', 'IBM', 'Apple']
[s.lower() for s in L]

.以及利用if语句是算法更灵活,如下例中,合理屏蔽掉18这个非字符串数据:

L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [s.lower() for s in L1 if isinstance(s, str) == True]

二、生成器Generator

有一句至理名言,必须牢记:

生成器保存的是算法。

2.1生成器的创建

生成器的创建很简单。将列表生成式的[]换成()即可

例如,创建一个能够生成x平方的列表的生成器如下:

g = (x * x for x in range(10))

2.2生成器元素的显示:

生成器保存的是生成算法,而非数据本身,因此,它与生成式不同,它并不携带任何数据,因此,想用print的形式是无法输出任何数据的,只能输出一个表示本对象是一个生成器对象的提示。如下:

g = (x * x for x in range(10))
print(g)

结果为:
<generator object <genexpr> at 0x00000252A2AC1690>

生成器像是一个携带着数据的宝盒,需要一把钥匙触发机关,他才会持续不断的根据算法产生数据并输出。这种触发算法的钥匙最常用的两个是:1.next方法,2.for循环的遍历。

2.2.1next方法

next方法会触发一次算法的计算,一次next就会激发一次算法的计算产生下一个数据。例如:

对于生成器:

g = (x * x for x in range(10))

如果:

next(g)
next(g)

则会输出:

1

4

两个数据。

2.2.2for循环遍历

使用for循环遍历,可以持续触发算法,并输出所有算法能覆盖的数据

例如:

对于生成器:

g = (x * x for x in range(10))

如果:

for n in g:
     print(n)

则会输出所有的数列元素1,4,9,16等。

2.3生成器的函数实现形式

 当推算算法比较复杂,可以用函数来实现。

如Fibonacci数列,很显然,这个算法写不到一行上去。没办法用小括号()的形式来展现算法,可以使用函数的形式来实现生成器。Fibonacci数列的普通函数实现如下:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

这有个小细节,其中的

a,b = b, a + b的意义是,先将右边的值全部算出,再分别赋值给左边的变量。

相当于:

temp1 = b

temp2 = a + b

a = temp1

b = temp2

而不是:

b = a + b

a = b

自己体会一下区别。

2.3.1 yield的用法

上面的Fibonacci数列的普通函数稍加改造,就可以变成一个生成器函数。

关键在于yield关键字。

先上案例程序:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

将普通函数中的print语句,换成yield。

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

函数是顺序执行,遇到return或最后一行返回。

包含yield关键字的生成器函数的执行逻辑如下:

Generator每次调用next()时候执行,遇到yield返回。再次执行,从上一次返回的yield处继续执行。

刚刚构建的生成器fib,遇到next调用时,就生成一个fibnacci数列的元素。

g = fib(10)
next(g)
next(g)
next(g)
next(g)

执行结果为:

1

1

2

3

再举一个例子:

写一个generator依次返回数字1,3,5

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)

可以利用next择机输出(或者说延迟输出)想要的数据

o = odd()
next(o)
next(o)
next(o)

得到的结果为:
step 1
1
step 2
3
step 3
5

如果再使用next(o),则会产生异常。

Traceback (most recent call last):   File “<stdin>”, line 1, in <module> StopIteration

2.4生成器Generator的作用

生成器和生成式,好像差不多啊。

生成器函数和普通函数,好像也差不多啊。

2.4.1算法的滞后输出

生成器的最大用途,是可以让算法的输出滞后,在用户需要的时机,缓缓输出。可以通过上面的例子来推敲和理解一下。

2.4.2无限数据的有限表达

生成器让类似于“自然数集”这样的无限数据生成算法,有了有限输出的空间。这一点尤为关键。因为算法必须是有限的,无限的算法是无意义的。毕竟,无论是时间还是空间,都必须是有限的,才能实施下去。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
社会演员多的头像社会演员多普通用户
上一篇 2023年6月25日
下一篇 2023年6月25日

相关推荐