python-生成器

1.列表生成式

需求:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 要求你把列表里的每个值加1

方式一:

>>> a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for index,i in enumerate(a):
...     a[index] += 1
...
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

方式二:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = list(map(lambda x:x+1, a))

print(a)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

方式三: 列表生成式

>>> a = [i+1 for i in range(10)]
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>>

2.python生成器

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

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

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
<generator object <genexpr> at 0x000000000261B258>

创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

我们可以打印generator 中的每个元素 ,next(g)

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

generator 保存的是算法,每次调用next(g)计算并返回每个元素值,,直到最后一个元素后再next(g),会报StopIteration 的错;

因为generator是可迭代对象,所以我们可以使用 for 循环来处理:

>>> g = (x*x for x in range(10))
>>> for i in g:
...     print(i)
...
0
1
4
9
16
25
36
49
64
81
>>>

我们可以看到,for循环 g 不会在最后报错


其实,range() 就是一个生成器

>>> range(1000000000)
range(0, 1000000000)
>>>

range(1000000000) 后,并没有打印出生成的列表,而是生成了一个公式算法,并没有生成这个列表,在我们真正使用的时候才会不断生成,当然这是在python3 中。

在python2中,range(10) 后,会把列表直接打印出来,但是python2.7中有一个 xrange(10) ,和python3中的 range()是一样的


用函数写一个生成器

#斐波那契数列

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

fib(10) # 1 1 2 3 5 8 13 21 34 55

改动一下,把print改为yield

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

f = fib(10)  # turn fuction into a generator
# 无打印结果,未执行
next(f)  # bofore yield......

next(f)一下,

next(f)
next(f)

打印结果:

# bofore yield......
# after yield.......
# bofore yield......

注意:

  • yield b ,把函数的执行过程冻结在这一步,并且把b的值 返回到外面的 next(f),这里可以把函数里面每一步的值返回出来,这一点很有用,在一些场景中可以利用
  • 函数只要加 yidld,函数就会冻结,不再执行,然后生成一个生成器对象

def range2(n):

    count = 0
    while count < n:
        print('count:',count)
        count += 1
        yield count #
    print('------')
    return 'end........'

new_range = range2(3)
n1 = next(new_range)
n2 = next(new_range)
n3 = next(new_range)
n4 = next(new_range)
print(n3)
print(n4)

打印结果:

Traceback (most recent call last):
  File "D:/Python/python_learning/luffy/code/part2/函数/生成器4.py", line 18, in <module>
    n4 = next(new_range)
StopIteration: end........
count: 0
count: 1
count: 2
------

注意:只要函数中有 yield ,

1.函数名加() 就得到了一个生成器,

2.return在生成器中,代表生成器的终止,

3.在运行到最后的时候,就会报出异常,此时,return的内容会以 StopIteration: end........ 的异常方式反馈


def range2(n):

    count = 0
    while count < n:
        print('count:',count)
        count += 1
        # yield count
        sign_str = yield count
        print('sign_str:',sign_str)
    print('------')
    return 'end........'

new_range = range2(3)
n1 = next(new_range)
print("do someting else...")
new_range.send("stop")

打印:

count: 0
do someting else...
sign_str: stop
count: 1

1.send,唤醒并继续执行

2.发送一个信息到生成器内部

我们可以根据send到生成器内部的内容做一些事情,比如:

def range2(n):

    count = 0
    while count < n:
        print('count:',count)
        count += 1
        # yield count
        sign_str = yield count
        if sign_str == 'stop':
            break
    return 'end........'

new_range = range2(3)
n1 = next(new_range)
print("do someting else...")
new_range.send("stop")

打印:

Traceback (most recent call last):
  File "D:/Python/python_learning/luffy/code/part2/函数/生成器6.py", line 19, in <module>
    new_range.send("stop")
StopIteration: end........
count: 0
do someting else...

newt(new_range) 实际上是发了一个 'None' 到生成器内部

原文地址:https://www.cnblogs.com/friday69/p/9246483.html