当前位置 : 主页 > 编程语言 > python >

python基础(part16)--生成器

来源:互联网 收集:自由互联 发布时间:2022-06-15
鄙人学习笔记 开发工具:Spyder 文章目录 ​​生成器generator​​ ​​生成器函数​​ ​​举个例子1(迭代器--过渡--生成器)​​ ​​举个例子2​​ ​​内置生成器​​ ​​举个例

鄙人学习笔记
开发工具:Spyder



文章目录

  • ​​生成器generator​​
  • ​​生成器函数​​
  • ​​举个例子1(迭代器-->过渡-->生成器)​​
  • ​​举个例子2​​
  • ​​内置生成器​​
  • ​​举个例子1​​
  • ​​举个例子2​​
  • ​​枚举函数enumerate​​
  • ​​zip​​
  • ​​生成器表达式​​
  • ​​举个例子1​​
  • ​​举个例子2​​



生成器generator

  • 定义

能够动态(循环一次计算一次返回一次)提供数据的可迭代对象。

  • 作用

在循环过程中,按照某种算法推算数据,不必创建容器,存储完整的结果,从而节省内存空间。数据量越大,优势越明显。

备注:以上生成器的操作,也称为惰性操作/延迟操作,通俗的讲就是在需要的时候才计算结果,而不是一次构建出所有结果。

生成器函数

  • 定义

生成器函数是含有yield语句的函数,生成器函数的返回值为生成器对象。只要我们的函数中有yield,那么这个函数就是生成器函数。

  • 语法

创建生成器函数:

def 生成器函数名():
...
yield 数据
...

调用生成器函数:

my_iter = 生成器函数名()

for 变量名 in my_iter:
语句
  • 说明

①调用生成器函数将返回一个生成器对象my_iter时,不执行生成器函数体。在for循环内,调用​​__next__()​​​方法时,才执行函数体。
②yield翻译为”产生”或”生成”。

举个例子1(迭代器–>过渡–>生成器)

我们看一下,下面几行代码:

python基础(part16)--生成器_迭代

  • 运行说明

首先,当程序执行第24行代码,即调用生成器函数my_range()时,它不会去执行my_range()函数体里的内容,而是先返回给变量iter01一个生成器对象,之后程序会继续向下执行for循环语句:

python基础(part16)--生成器_迭代_02

当执行第25行语句时,for循环内部调用了​​__next__()​​​方法,这时才开始执行my_range()函数体里的内容:
python基础(part16)--生成器_python_03
当执行到yield语句时,程序又返回了,程序将yield右边的start作为​​​__next__()​​方法的返回值,给了变量item:

python基础(part16)--生成器_迭代_04
python基础(part16)--生成器_生成器_05
之后的程序运行内容,不言而喻。就不一一赘述了,有机会可以自己在python里,断点调试,观察一下~

举个例子2

我想在列表[1, 2, 5, 4, 6, 7, 9]中选出所有偶数,并且通过生成器函数来实现。

创建生成器函数:

def get_even(target):
for item in target:
if item % 2 == 0:
yield item

备用1:我们每次写生成器函数时,都要思考yield要返回什么内容.
备用2:我们什么时候使用生成器函数呢?在我们的方法/函数需要向外返回多个结果时,可以使用生成器函数.

调用生成器函数:

list01 = [1, 2, 5, 4, 6, 7, 9]
iter01 = get_even(list01)

for item in iter01:
print(item)

结果:
python基础(part16)--生成器_迭代_06

我们其实不用生成器函数,也可以拿到这个结果,比如以下代码。

创建函数:

def get_even01(target):
result = []
for item in target:
if item %2 == 0:
result.append(item)
return result

调用函数:

list01 = [1, 2, 5, 4, 6, 7, 9]
iter01 = get_even01(list01)

for item in iter01:
print(item)

结果:
python基础(part16)--生成器_for循环_07
这个结果,同上面利用生成器函数,得到的结果一样。

那么我们为啥要用生成器函数呢?他的优势是啥呢?

因为,如果调用get_even01()方法,则会执行get_even01()方法体,将所有结果存在内存中。但是,如果调用生成器函数get_even(), 则不会执行方法体,在内存中不会存储数据。我们用for循环,循环一次,也就是​​__next__()​​​一次,计算一次,返回一次。即,需要一次,做一次。这种生成器函数的操作就叫做:惰性操作/延迟操作。
所以,在数据量非常大的情况下,如果不用生成器函数进行操作,则,我们的内存很快就会被占满。

内置生成器

我们先举两个例子。

举个例子1

我们看一段代码:

python基础(part16)--生成器_for循环_08
结果:
python基础(part16)--生成器_python_09

我们再写一个自定义函数my_enumerate,具有和上述例子中,enumerate函数所演示出来的相同的功能:

python基础(part16)--生成器_迭代_10

for循环测试一下:

python基础(part16)--生成器_python_11
结果:
python基础(part16)--生成器_迭代_12
嗯!一毛一样~

举个例子2

我们再看一段代码:

list01 = [0, 6, 7]
list02 = ["小白", "小黄", "大白"]

for item in zip(list01, list02):
print(item)

结果:
python基础(part16)--生成器_for循环_13

若我们在列表list02中再加一个元素,使两个列表元素不相同:
python基础(part16)--生成器_迭代_14
结果:

python基础(part16)--生成器_python_15
我们再写一个自定义函数my_zip,具有和上述例子中,zip函数所演示出来的相同的功能(不考虑两个列表元素不相同的情况):

def my_zip(list01, list02):
for index in range(len(list01)):
yield (list01[index], list02[index])

for循环测试一下:

list01 = [0, 6, 7]
list02 = ["小白", "小黄", "大白"]

for item in my_zip(list01, list02):
print(item)

结果:
python基础(part16)--生成器_for循环_16

注意:我们称这种必须由​​__next__()​​驱动的函数(enumerate() / zip()),叫做内置生成器。

枚举函数enumerate

  • 语法
for 变量 in enumerate(可迭代对象):
语句

for 索引, 元素 in enumerate(可迭代对象):
语句
  • 作用

遍历可迭代对象时,可以将索引与元素组合为一个元组。

zip

  • 语法
for 变量 in zip (可迭代对象1, 可迭代对象2….):
语句
  • 作用

将多个可迭代对象中对应的元素组合成一个个元组,生成的元组个数由元素数量最小的可迭代对象决定。

生成器表达式

  • 定义

用推导式形式创建生成器对象。

  • 语法
变量 = (表达式 for 变量 in 可迭代对象 [if 真值表达式])

举个例子1

如果我们有一个列表list01=[2,3,4,6], 如下图所示:

python基础(part16)--生成器_生成器_17
我们想得到列表内的每个元素的平方,该咋整呢?

方式1:

python基础(part16)--生成器_python_18

方式2(列表推导式):

python基础(part16)--生成器_python_19
我们print一下result得到结果:
python基础(part16)--生成器_迭代_20

备注1:方式1的代码是列表推导式的传统写法。
备注2:对于推导式,我们还学习了字典推到式,集合推导式。但是没有元组推导式,这是为什么捏?这是因为,列表、字典、集合都是可变对象,可以不断增加元素。而元组是不可变的。

我们再写一段代码:
python基础(part16)--生成器_生成器_21
这个可不是”元组推导式”(压根没有元组推导式),这是生成器表达式。

这时,我们再print一下result:
python基础(part16)--生成器_生成器_22

得到一个生成器对象。生成器的本质是一个惰性查找机制,要调用它,就要用​​__next__()​​​, 也就是可以用for循环:
python基础(part16)--生成器_for循环_23

得到结果:
python基础(part16)--生成器_python_24

我们可以写一个与这个生成器表达式功能相同的生成器函数:
python基础(part16)--生成器_生成器_25
for循环:
python基础(part16)--生成器_python_26
得到结果:
python基础(part16)--生成器_迭代_27
嗯!与利用生成器表达式运行的结果相同。

这时候我们就想问:列表推导式和生成器表达式有啥区别?这个问题就相当于问:列表推导式的传统写法与生成器函数有啥不同?

答:区别就是列表推导式的传统写法会用一个列表result存储所有结论,占用内存。而生成器函数是惰性操作,被调用一次​​__next__()​​, 才会计算并返回一次。

举个例子2

分别使用列表推导式和生成器表达式,获取列表[2, 3, 4, 5, 6]中,大于3的数据。

代码:

list01 = [2, 3, 4, 5, 6]

result01 = [item for item in list01 if item > 3]
result02 = (item for item in list01 if item > 3)

for item in result01:
print(item)

print("---------")

for item in result02:
print(item)

结果:
python基础(part16)--生成器_for循环_28

备注:别看列表推导式和生成器表达式的代码极其相似,但是他们内部却大有不同,【​​result01 = [item for item in list01 if item > 3]​​​】是执行所有操作,保存所有结果;【​​result02 = (item for item in list01 if item > 3​​】是返回了一个生成器对象 。我们也可以通过断点调试,去加深理解。

上一篇:python基础(part17)--函数式编程
下一篇:没有了
网友评论