Python:理解生成器,并用生成器构建斐波那契数列和杨辉三角

2020-10-22  本文已影响0人  dex0423

1. 生成器函数

1.1. 什么是生成器

2. 迭代器与生成器的关系

【说明】:很多文章在介绍生成器的时候,上来就引入 yield,说使用 yiled 关键字的函数就是生成器, 但很多人并不能理解 yield 的含义,这种用新概念解释新概念的方式,对于新人来说很不友好,这里我先从最基本的迭代器开始,介绍如何用迭代器构造一个生成器,进而引入 yiled 概念。

class IteraleSample(object):
    '''
    创建一个类对象,并定义 __iter__、__next__ 两个方法
    '''
    def __init__(self, max_iter_num):
        '''
        max_iter_num: 迭代器最大迭代次数
        '''
        self.current = 0
        self.max_iter_num = max_iter_num

    def __iter__(self):
        return self

    def __next__(self):
        # pass
        if self.current < self.max_iter_num:
            self.current += 1
        else:
            raise StopIteration
        return self.current


class Generator(object):
    def __init__(self, max_generate_num):
        '''
        max_generate_num: 生成器最大生成次数
        '''
        self.max_generate_num = max_generate_num
        self.iterator_sample = IteraleSample(self.max_generate_num)

    def __iter__(self):
        return self.iterator_sample

    def __next__(self):
        return self.iterator_sample.__next__() * 10


gen = Generator(10)    # 实例化生成器,并赋值最大迭代次数为 10

print(next(gen))       # 使用 next 函数迭代元素
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))        # 此处,共计 next 迭代 11 次,超出了生成器的最大迭代次 10
10
20
30
40
50
60
70
80
90
100
Traceback (most recent call last):
  File "C:\Users\Administrator\Desktop\iterator_test.py", line 94, in <module>
    print(next(gen))
  File "C:\Users\Administrator\Desktop\iterator_test.py", line 76, in __next__
    return self.iterator_sample.__next__() * 10
  File "C:\Users\Administrator\Desktop\iterator_test.py", line 63, in __next__
    raise StopIteration
StopIteration

3. 如何构造生成器

注意:生成器函在被 next 调用之前,必须将生成器函数赋值于某个变量,next 调用的是被赋值后的变量,而不是函数本身,如:下面代码中的 result = fun_gen(),这一步一定不能省,next 调用的是 result,不是 fun_gen();

>>> def fun_gen():
...     print("第一次 yield")
...     yield 1
...     print("第二次 yield")
...     yield 2
...     print("第三次 yield")
...     yield 3
...
>>> result = fun_gen()                # 注意:必须将生成器函数赋值于某个变量!!!
>>> next(result)                      # next 调用的是被赋值后的变量,而不是函数本身;
第一次 next
1
>>> next(result)                      # next 调用的是被赋值后的变量,而不是函数本身;
第二次 next
2
>>> next(result)                      # next 调用的是被赋值后的变量,而不是函数本身;
第三次 next
3
>>> def fun_gen():
...     print("第一次 yield")
...     yield 1
...     print("第二次 yield")
...     yield 2
...     print("第三次 yield")
...     yield 3
...
>>>
>>> result = fun_gen()
>>> next(result)
第一次 yield
1
>>> next(result)
第二次 yield
2
>>> next(result)
第三次 yield
3
>>> next(result)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration                          # 没有更多的元素,next() 动作抛出 StopIteration 的错误
>>> def fun_gen():
...     print("第一次 yield")
...     yield 1
...     print("第二次 yield")
...     yield 2
...     print("第三次 yield")
...     yield 3
...
>>>
>>> for i in fun_gen():
...     print(i)
...
第一次 yield
1
第二次 yield
2
第三次 yield
3

4. 生成器函数 与 普通函数 的区别

5. 用生成器构造斐波那契数列

5.1. 斐波那契数列简介

-- 斐波那契数列,是有一系列整数组成的数列;
-- 这一数列任意位置上的数值与后一位相加,等于第三位数值;
-- 示例:

0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , ...

5.2. 普通方法构造斐波那契数列

def feb_1(index):
    """
    斐波那契数列生成函数
    """
    if index <= 2:
        return 1
    elif index > 2 and index < 101:
        return feb_1(index-1) + feb_1(index-2)

def test_time(max_index):
    """
    测试上面 斐波那契数列函数的执行情况
    """
    starttime = datetime.datetime.now()  # 记录开始时间
    print("最大值为 : {}\n结果值为:{}".format(max_index, feb_1(max_index)))
    endtime = datetime.datetime.now()  # 记录结束时间
    print("运行时间为:{}".format(endtime - starttime))  # 以秒为单位,计算运行时长

-- 方法缺陷:
(1)每次只能获取一个末位的数值,而不是整个数列;
(2)如果数值太大,运行时间会变得非常的长;

print(test_time(10))

print(test_time(20))

print(test_time(30))

print(test_time(40))

最大值为 : 10
结果值为:55
运行时间为:0:00:00
None

最大值为 : 20
结果值为:6765
运行时间为:0:00:00.003998
None

最大值为 : 30
结果值为:832040
运行时间为:0:00:00.469920 
None

最大值为 : 40
结果值为:102334155
运行时间为:0:00:57.805053       # 此处,max_index=40,运行时间就已经需要 57 秒,相比前面的时间花费增速是几何式的,
None

-- 如上面的例子,max_index = 30 的时候程序运行时常只需要 0:00:00.749871 秒,到了 max_index = 40 的时候就已经需要 0:00:57.805053 秒,这种耗时的增速在实际生产中是不能接受的;

def feb_2(max_index):
    """
    斐波那契数列生成函数
    """
    result_list = []          # 此处使用 list 作为一个容器存储生成的数值,如果数值足够大这个 list 将会占用大量的的内存
    n, a, b = 0, 0, 1
    while n < max_index:
        result_list.append(b)
        a,b = b, a+b
        n += 1
    return "最大值为 : {}\n结果值为:{}".format(max_index, result_list)

def test_time(max_index):
    """
    测试上面 斐波那契数列函数的执行情况
    """
    starttime = datetime.datetime.now()  # 记录开始时间
    print(feb_2(max_index))
    endtime = datetime.datetime.now()  # 记录结束时间
    print("运行时间为 : {}".format(endtime - starttime))  # 以秒为单位,计算运行时长

-- 该方法的优点:相比与前面的方法,程序运行耗费的时间相对较快;

test_time(10)

test_time(20)

test_time(30)

test_time(40)

最大值为 : 10
结果值为:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
运行时间为 : 0:00:00.001000

最大值为 : 20
结果值为:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
运行时间为 : 0:00:00

最大值为 : 30
结果值为:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040]
运行时间为 : 0:00:00

最大值为 : 40
结果值为:[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]
运行时间为 : 0:00:00

-- 该方法的缺陷:该方法使用 result_list = [] 作为一个容器存储生成的数值,如果数值足够大这个 list 将会占用大量内存空间,简言之:如果数值过大,会占用大量内存
-- 有兴趣的小伙伴可以尝试 max_index=10000 时程序的执行情况,看一看程序内存占用情况;
-- 请注意:你的电脑可能会被卡死!!!

test_time(10000)    # 注意:你的电脑可能会被卡死

5.3. 生成器构造斐波那契数列

import datetime


def fib_3(max_index):
    """
    使用 yiled 生成器构造的斐波那契数列生成函数
    """
    n, a, b = 0, 0, 1
    while n < max_index:
        yield b                                                 # 此处使用 yiedl b 
        a,b = b, a+b
        n += 1


def test_time(max_index):
    """
    测试上面 斐波那契数列函数的执行情况
    """
    starttime = datetime.datetime.now() 
    for i in fib_3(max_index):                                   # 使用 for ... in ... 遍历打印所有的生成结果
        print(i)
    endtime = datetime.datetime.now() 
    print("运行时间为 : {}".format(endtime - starttime))


test_time(10)        
test_time(100)
test_time(1000)
test_time(10000) 
# 运行的时间如下所示:

运行时间为 : 0:00:00
运行时间为 : 0:00:00
运行时间为 : 0:00:00.001000
运行时间为 : 0:00:00.004998

6. 生成器函数构建杨辉三角

20190307104242486.png
def triangles(max_index):        # max_index 表示最大行数
    L = [1]
    i = 1
    while i <= max_index:        # 进入持续循环
        yield L[:len(L)]         # 输出数组
        L.append(0)              # 为数组添加最后一位
        L = [L[j - 1] + L[j] for j in range(len(L))]
        i += 1

for i in triangles(10):          # 使用 for 循环打印结果值
    print(i)

[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]
上一篇下一篇

猜你喜欢

热点阅读