小白学 Python(21):生成器基础

人生苦短,我选Pythonhtml

前文传送门python

小白学 Python(1):开篇git

小白学 Python(2):基础数据类型(上)github

小白学 Python(3):基础数据类型(下)算法

小白学 Python(4):变量基础操做数据结构

小白学 Python(5):基础运算符(上)多线程

小白学 Python(6):基础运算符(下)函数

小白学 Python(7):基础流程控制(上)spa

小白学 Python(8):基础流程控制(下)线程

小白学 Python(9):基础数据结构(列表)(上)

小白学 Python(10):基础数据结构(列表)(下)

小白学 Python(11):基础数据结构(元组)

小白学 Python(12):基础数据结构(字典)(上)

小白学 Python(13):基础数据结构(字典)(下)

小白学 Python(14):基础数据结构(集合)(上)

小白学 Python(15):基础数据结构(集合)(下)

小白学 Python(16):基础数据类型(函数)(上)

小白学 Python(17):基础数据类型(函数)(下)

小白学 Python(18):基础文件操做

小白学 Python(18):基础文件操做

小白学 Python(19):基础异常处理

小白学 Python(20):迭代器基础

生成器

咱们前面聊过了为何要使用迭代器,各位同窗应该还有印象吧(说没有的就太过度了)。

列表太大的话会占用过大的内存,可使用迭代器,只拿出须要使用的部分。

生成器的设计原则和迭代器是类似的,若是须要一个很是大的集合,不会将元素所有都放在这个集合中,而是将元素保存成生成器的状态,每次迭代的时候返回一个值。

好比咱们要生成一个列表,能够采用以下方式:

list1 = [x*x for x in range(10)]
print(list1)

结果以下:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

若是咱们生成的列表很是的巨大,好比:

list2 = [x*x for x in range(1000000000000000000000000)]

结果以下:

Traceback (most recent call last):
  File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <module>
    list2 = [x*x for x in range(1000000000000000000000000)]
  File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 3, in <listcomp>
    list2 = [x*x for x in range(1000000000000000000000000)]
MemoryError

报错了,报错信息提示咱们存储异常,而且整个程序运行了至关长一段时间。友情提醒,这么大的列表建立请慎重,若是电脑配置不够颇有可能会将电脑卡死。

若是咱们使用生成器就会很是方便了,并且执行速度嗖嗖的。

generator1 = (x*x for x in range(1000000000000000000000000))
print(generator1)
print(type(generator1))

结果以下:

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

那么,咱们使用了生成器之后,怎么读取生成器生成的数据呢?

固然是和以前的迭代器同样的拉,使用 next() 函数:

generator2 = (x*x for x in range(3))
print(next(generator2))
print(next(generator2))
print(next(generator2))
print(next(generator2))

结果以下:

Traceback (most recent call last):
  File "D:/Development/Projects/python-learning/base-generator/Demo.py", line 14, in <module>
    print(next(generator2))
StopIteration

直到最后,抛出 StopIteration 异常。

可是,这种使用方法咱们并不知道何时会迭代结束,因此咱们可使用 for 循环来获取每生成器生成的具体的元素,而且使用 for 循环同时也无需关心最后的 StopIteration 异常。

generator3 = (x*x for x in range(5))
for index in generator3:
    print(index)

结果以下:

0
1
4
9
16

generator 很是的强大,本质上, generator 并不会取存储咱们的具体元素,它存储是推算的算法,经过算法来推算出下一个值。

若是推算的算法比较复杂,用相似列表生成式的 for 循环没法实现的时候,还能够用函数来实现。

好比咱们定义一个函数,emmmmmm,仍是简单点吧,你们领会精神:

def print_a(max):
    i = 0
    while i < max:
        i += 1
        yield i

a = print_a(10)
print(a)
print(type(a))

结果以下:

<generator object print_a at 0x00000278C6AA5CC8>
<class 'generator'>

这里使用到了关键字 yieldyieldreturn 很是的类似,均可以返回值,可是不一样的是 yield 不会结束函数。

咱们调用几回这个用函数建立的生成器:

print(next(a))
print(next(a))
print(next(a))
print(next(a))

结果以下:

1
2
3
4

能够看到,当咱们使用 next() 对生成器进行一次操做的时候,会返回一次循环的值,在 yield 这里结束本次的运行。可是在下一次执行 next() 的时候,会接着上次的断点接着运行。直到下一个 yield ,而且不停的循环往复,直到运行至生成器的最后。

还有一种与 next() 等价的方式,直接看示例代码吧:

print(a.__next__())
print(a.__next__())

结果以下:

5
6

接下来要介绍的这个方法就更厉害了,不只能迭代,还能给函数再传一个值回去:

def print_b(max):
    i = 0
    while i < max:
        i += 1
        args = yield i
        print('传入参数为:' + args)

b = print_b(20)
print(next(b))
print(b.send('Python'))

结果以下:

1
传入参数为:Python
2

上面讲了这么多,可能各位还没想到生成器能有什么具体的做用吧,这里我来提一个——协程。

在介绍什么是协程以前先介绍下什么是多线程,就是在同一个时间内能够执行多个程序,简单理解就是你平时可能很常常的一边玩手机一边听音乐(毫无违和感)。

协程更贴切的解释是流水线,好比某件事情必须 A 先作一步, B 再作一步,而且这两件事情看起来要是同时进行的。

def print_c():
    while True:
        print('执行 A ')
        yield None
def print_d():
    while True:
        print('执行 B ')
        yield None

c = print_c()
d = print_d()
while True:
    c.__next__()
    d.__next__()

结果以下:

...
执行 A 
执行 B 
执行 A 
执行 B 
执行 A 
执行 B 
执行 A 
执行 B 
执行 A 
执行 B 
...

由于 while 条件设置的是永真,因此这个循环是不会停下来的。

这里咱们定义了两个生成器,而且在一个循环中往复的调用这两个生成器,这样看起来就是两个任务在同时执行。

最后的协程可能理解起来稍有难度,有问题能够在公众号后台问我哦~~~

示例代码

本系列的全部代码小编都会放在代码管理仓库 Github 和 Gitee 上,方便你们取用。

示例代码-Github

示例代码-Gitee

原文出处:https://www.cnblogs.com/babycomeon/p/11854666.html

相关文章
相关标签/搜索