关于 python 的生成器

本贴最后更新于 796 天前,其中的信息可能已经时过境迁

生成器

列表推导式:直接生成列表

List1=[x for x in range[1,11]]
print(List1)

生成器

什么是生成器?

通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator

生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。

生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器
python中的生成器
要创建一个generator,有很多种方法,第一种方法很简单,只有把一个列表生成式的[]中括号改为()小括号,就创建一个generator

#列表生成式

lis = [x*x for x in range(10)]
print(lis)

#生成器

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

结果:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

<generator object at 0x000002A4CBF9EBA0>

那么创建lis和generator_ex,的区别是什么呢?从表面看就是[ ]和(),但是结果却不一样,一个打印出来是列表(因为是列表生成式),而第二个打印出来却是 at 0x000002A4CBF9EBA0>,那么如何打印出来generator_ex的每一个元素呢?

如果要一个个打印出来,可以通过next()函数获得generator的下一个返回值:

#生成器

generator_ex = (x*x for x in range(10))
print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

print(next(generator_ex))

结果:

0

1

4

9

16

25

36

49

64

81

Traceback (most recent call last):

File "列表生成式.py", line 42, in

print(next(generator_ex))

StopIteration

大家可以看到,generator保存的是算法,每次调用next(generaotr_ex)就计算出他的下一个元素的值,直到计算出最后一个元素,没有更多的元素时,抛出StopIteration的错误,而且上面这样不断调用是一个不好的习惯,正确的方法是使用for循环,因为generator也是可迭代对象:

#生成器

generator_ex = (x*x for x in range(10))
for i in generator_ex:

print(i)

结果:

0

1

4

9

16

25

36

49

64

81

所以我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心StopIteration的错误,generator非常强大,如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如著名的斐波那契数列,除第一个和第二个数外,任何一个数都可以由前两个相加得到:

1,1,2,3,5,8,12,21,34.....

斐波那契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

#fibonacci数列

def fib(max):

  n,a,b =0,0,1

  while n < max:

    a,b =b,a+b

    n = n+1

    print(a)

  return 'done'

a = fib(10)

print(fib(10))

也就是说上面的函数也可以用generator来实现,上面我们发现,print(b)每次函数运行都要打印,占内存,所以为了不占内存,我们也可以使用生成器,这里叫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'

a = fib(10)

print(fib(10))
print([i for i in a])

yield函数可以让当前函数停下来,等需要调用的时候再执行,资源消耗最小的。

回帖
请输入回帖内容 ...