关于 python 的生成器

生成器

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

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 函数可以让当前函数停下来,等需要调用的时候再执行,资源消耗最小的。

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