python生成器

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

有没有一种办法,可以不必创建完整的列表,而我们需要访问的元素,可以通过一边循环一边计算的机制得到?答案是肯定的。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

创建generator的方法:
(一)将列表生成式中的 [ ] 改成()

#生成一个列表
L= [x*x for x in range(10)]
#生成一个generator
g = (x*x for x in range(10))

创建列表和生成器的区别仅在于最外层的 [ ] 和()。 [ ] 是创建一个列表,而()是创建一个生成器。
列表可以直接打印出来,而生成器不能直接打印。

print(L)
>>>[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(g)
>>><generator object <genexpr> at 0x0000023E41E93A98>

生成器不能直接打印,如果要一个一个地打印生成器中的元素,可以通过next()函数获得生成器的下一个返回值。

print(next(g))
>>>0
print(next(g))
>>>1
print(next(g))
>>>4
print(next(g))
>>>9
print(next(g))
>>>16
print(next(g))
>>>25
print(next(g))
>>>36
print(next(g))
>>>49
print(next(g))
>>>64
print(next(g))
>>>81

#直到计算到最后一个元素,没有更多元素时,抛出StopIteration的错误。

print(next(g))
>>>Traceback (most recent call last):
  File "F:/python_files/ex1.py", line 14, in <module>
    print(next(g))
StopIteration

不断调用next()函数似乎过于变态,更好的方法是使用for循环。

for n in g:
    print(n)
>>>
0
1
4
9
16
25
36
49
64
81

用for循环来迭代生成器,不需要考虑StopIteration的错误。

(二)生成器函数定义中采用yield关键字

如果一个函数定义中采用了yield关键字,那么这个函数就不再是一个普通函数,而是一个生成器。

#定义一个普通的计算斐波那契数列的函数
def fib1(max):
    n,a,b = 0,0,1
    while n<max:
        print(b)
        a,b = b,a+b
        n = n+1
    return 'done'
fib1(6)
>>>
1
1
2
3
5
8
#定义一个生成器
def fib2(max):
    n,a,b = 0,0,1
    while n<max:
        yield b
        a,b = b,a+b
        n = n+1
    return 'done'
f = fib2(6)
f
>>>无输出,也不报错。

print(f)
>>><generator object fib2 at 0x000001DAD3C83A98>

next(f)
>>>无输出,也不报错。

print(next(f))
>>>1
print(next(f))
>>>1
print(next(f))
>>>2
……

更简单的迭代方法

for n in f:
    print(n)
>>>
1
1
2
3
5
8

变成生成器的函数和普通函数的区别:
普通函数是顺序执行,遇到return语句或者最后一行语句就返回。
变成生成器的函数,在每次调用next()函数时执行,遇到yield语句就返回。再次执行时从上次返回的yield语句处继续执行.

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)
    
#生成一个生成器对象(调用该生成器前,首先要生成一个生成器对象)
o = odd()

#用next()函数进行迭代
next(o)
>>>step 1
next(o)
>>>step 2
next(o)
>>>step 3
next(o)
>>>StopIteration

print(next(o))
>>>
step 1
1

print(next(o))
>>>
step 2
3

print(next(o))
>>>
step 3
5

print(next(o))
>>>
StopIteration

但是用for循环调用生成器时,发现拿不到生成器的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

 g = fib2(6)
 while True:
     try:
         x = next(g)
         print('g:', x)
     except StopIteration as e:
         print('Generator return value:', e.value)
          break
          
>>>
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done

练习:
杨辉三角定义如下:


          1
         / \
        1   1
       / \ / \
      1   2   1
     / \ / \ / \
    1   3   3   1
   / \ / \ / \ / \
  1   4   6   4   1
 / \ / \ / \ / \ / \
1   5   10  10  5   1

把每一行看做一个list,试写一个generator,不断输出下一行的list:

def triangles():
    n = 0
    L = [1]
    while True:
        yield L
        L = [L[x] + L[x + 1] for x in range(n)]
        L.append(1)
        L.insert(0, 1)
        n = n + 1

n = 0
results = []
for t in triangles():
    print(t)
    results.append(t)
    n = n + 1
    if n == 10:
        break
if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
测试通过!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值