生成器与斐波拉契数列

生成器

通过表达式构造生成器

通过列表生成式,我们可以用简单的一行代码生成列表

print([i for i in range(10)])
print(type([i for i in range(10)]))

输出结果为:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
<class 'list'>

如果我们把列表生成式中[]符号改写成(),生成的就不再是列表,而是生成器(generator)。

print((i for i in range(10)))
print(type((i for i in range(10))))

输出结果为:

<generator object <genexpr> at 0x0000013760BDF228>
<class 'generator'>

可以看出,当我们print一个生成器的时候,python并没有输出每个元素,而是产生了一个<generator object <genexpr> at 0x0000013760BDF228>。这个输出告诉你了这是一个生成器并告诉了它的地址。

获取生成器的值
通过next()函数

然而我们如果想要获取每个元素的值呢,那么我们就必须要调用next()函数,next()函数可以把生成器的每个元素逐个返回出来。如果我们要打印我们刚才定义的生成器,那么我们就要调用10次next()函数。需要注意的是next()函数只是返回元素,就好像return一样,所以在编译环境里还需要用print进行打印。

在调用next()函数前,我们首先要实例化一个生成器。

g = (i for i in range(10))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))

输出结果为

0
1
2
3
4
5
6
7
8
9

从以上代码中可以看到,我们一共调用了10次next()函数,通过print()把每个生成器的元素都给打印了出来。如果我们调用了第11次next()函数的话,就会出现报错StopIteration

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

因此通过next()函数来获取生成器的值是很繁琐的。因此我们一般通过for循环来或许元素。

通过for循环
g = (i for i in range(10))

for i in g:
	print(i)

输出结果为:

0
1
2
3
4
5
6
7
8
9

这样看是不是简单了很多了。事实上生成器就是一个迭代器,for循环可以用于任何可迭代的对象。

生成器与列表的区别

列表存储着一系列元素,如果元素非常的多,那么就会占用大量的内存。

生成器相比于列表,其实更像是一个规则,它可以根据某种设定好的规则一个个推算出下一个元素的值。因为它不需要占用大量的内存空间。

上述的生成器的例子比较简单,我们可以通过函数制定一种更加复杂规则。例如我们要推算一个自然数的集合,自然数是无穷的,所以用列表是无法存储的。然而用生成器的话,我们只要制定一个种规则,如从0开始每个元素是前一个元素的值加1,通过Whilenext()我相信是可以一直把自然数打出来的。(数学上把自然数定义为可数数,其实就是用一种规则可数,但还是无穷的。)

通过函数构造生成器和斐波拉契数列

自然数的例子很简单。我们通过一个较为复杂的斐波拉契数列进行说明。的斐波拉契数列的规则就是通过计算前两个数的和获取第三个数,以此无限类推下去。
首先我们构造一个函数并调用它输出前6个值。

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

fib(6)
print(type(fib))
# print(type(fib(6)))

输出结果

1
1
2
3
5
8
<class 'function'>

这里定义的是一个fib是一个函数,而调用了fib(6)以后我们return是一个str类型,其实就是"done"

这还不是一个生成器,如果我们要把它改成一个生成器拿,其实只要把print(b)改成yield b就可以了。
yield函数类似一个return,会返回一个值,此处返回的就是b的值,它会在被next()函数调用时返回,可以通过print(next(f))打印出来。

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

fib(6)
print(type(fib))
print(type(fib(6)))

输出为:

<class 'function'>
<class 'generator'>

为了更好得说明,我在yield b前后分别加了两个打印。

从上面的代码看出,fib(6)的调用并没有任何输出。从type(fib(6))中也可以看出fib(6)已经是一个生成器了,而不是函数的调用了。
需要注意的是,fib仍然是一个函数。

因为如果我们还想获取其中的元素,就需要通过next()函数了。同样的,我们先实例化一个生成器。

f = fib(6)
print(next(f))

输出结果为:

before yield
1

从结果可以看出,我们调用了一次next(f)函数,fib函数并没有打印"after yield",也就说只执行到yield b,此时返回的b通过print打印出来是1

让我们再多调用几次next(f)函数。

f = fib(6)
print(next(f))
print(next(f))
print(next(f))

输出为:

before yield
1
after yield
before yield
1
after yield
before yield
2

可以看出,执行的效果是这样的,第一次调用next(f),函数执行到yield b停止,第二次调用next(f),函数从第一次yield b停止的地方继续开始执行,由于while循环的原因,循环执行到yield b再次停止,就这样周而复始执行下去。
如果这样子还不清楚,可以看下面这个廖雪峰大大python上的例子。直接上图。安利一下。
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值