Python的生成器

生成器介绍

生成器概念

若一个函数里面存在yield关键字,则该函数调用之后产生的返回值,就是一个生成器对象。


def func():
    print('第一次')
    print('函数')
    yield 1
    print('第二次')
    yield 2 
    print('第三次')
    yield 3 

g = func()  # 调用该函数产生的返回值,则是我们的生成器对象,即变量g
print(type(g)) # <class 'generator'>

生成器其实就是一个迭代器,生成器内置有__iter__方法和__next__方法, 当元素被取尽之后,也会报StopIteration错误。

Python的生成器插图

同样使用异常处理即可,当遇到了StopIteration错误,则直接停止循环。

while True:
    try:
        print(g.__next__())
    except StopIteration:
        break

yield关键字

若一个函数里面存在yield关键字,那么当我们再执行调用函数的时候,则不会立即去执行函数体的代码。而是将该函数挂起, 我们调用该函数,将返回值赋值给一个变量,那么这个变量就是一个生成器对象,之后我们调用该生成器对象下的__next__方法,则会运行我们函数里面第一个yield函数之前的代码。 yieldreturn不同的是,函数在遇到return的时候,则直接退出函数的执行了,而函数在遇到yield的时候,则直接在yield处将函数挂起。

一个函数里面存在yield关键字,那么该函数调用之后产生的返回值,则是我们的生成器对象。

def func():
    print('第一次')
    print('123')
    print('456')
    yield 1
    print('第二次')
    print('789')
    print('手拉手')
    yield 2 
    print('第三次')
    yield 3 

g = func()
print(g) # <generator object func at 0x7feb8026b390>
g.__next__() # 调用生成器下的__next__方法, 则会执行第一个yield关键字前的代码

以下为代码运行效果:

Python的生成器插图1

再次调用g.__next__(),则会执行第二yield关键字前的所有代码。

g = func()
print(g) # <generator object func at 0x7feb8026b390>
g.__next__() # 调用生成器下的__next__方法, 则会执行第一个yield关键字前的代码
g.__next__() # 调用生成器下的__next__方法, 则会执行第一个yield关键字前的代码

运行结果:

Python的生成器插图2

使用yield关键字,实现一个类似于range()函数的功能:

def my_range(start,stop,step=1):
    while start < stop:
        start += step
        yield start

res = my_range(1,10,2)
print(res.__next__()) # 2
print(res.__next__()) # 4
print(res.__next__()) # 6
print(res.__next__()) # 8

其实__iter____next__还可以这么调用,直接通过函数传参的形式调用,iter(可迭代对象)以及next(迭代器对象)

len('hello') # 'hello'.__len__() 


s1 = 'world'
iterator = iter(s1) # s1.__iter__()
print(next(iterator)) # iterator.__next__() 

yield表达式

yield也可以当做一个值,返回给一个变量,比如x = yield None

当一个函数存在yield关键字,那么该函数调用之后返回的返回值,就是一个生成器

def dog():
    print('dog 开始吃东西啦...')
    while True:
        x = yield 'x1ong' # x = "一根骨头"  yield "x1ong" 表示"x1ong"作为函数的返回值,并不会赋值给x,被send进来的值才会赋值给x。
        print('dog吃了%s'%(x))
g = dog() # 调用函数的返回值,赋值给g,此时g就是一个生成器

生成器下有一个send()方法,该方法可以将一个数据类型赋值给yield前面的变量。但是在使用send()方法之前,需要先send(None)才可以,也就是初始化一下,不然会报TypeError: can't send non-None value to a just-started generator意思是生成器刚被创建就被传入了一个非None的值,所以我们需要先send(None)一下,让程序停留在第一个yield关键字处。

先初始化这个生成器,即send(None)

g.send(None)  # 等同于next(g) 使用send之前需要先初始化生成器,即send(None)

之后再进行正常的send

g.send('一根骨头') # 此时"一根骨头",则会被赋值给变量x
g.send('肉包子') # 由于是死循环,前面的"一根骨头"被send之后,又开始停留在yield处,因此我们这边再次send(),那么将值会重新赋值给x,x就为"肉包子",send()也有next()的功能,所以会输出 dog吃了肉包子
g.send(True) # 同上

运行结果:

Python的生成器插图3

当一个生成器使用生成器.close()方法关闭之后,我们不能在使用send()next()方法,否则会报StopIteration错误。

yield表达式返回值

def bar():
    while True:
        x = yield '1111' # x = "我是被send进来的值"
        print(x)


g = bar()
return_value = g.send(None) # 等同于next(g)
print(return_value) # 111

# 只有被send进来之后的值才会被yield接受,从而赋值给变量x
g.send('我是send进来的值') # x = "我是被send进来的值"

# 只能send()进来一个值,但是该值可以是任意数据类型
# g.send('1',1) # 报错示例: send进来两个参数

三元表达式

三元表达式常常指的是,将if语句缩写在一行,易于代码的精简,同时可读性也不会差。

正常情况下,我们要写一个判断语句,则需要使用如下格式去写:

x = 123
y = 456
if x > y:
    print('x大于y')
else:
    print('y大于x')

可以发现,非常的繁琐,那么我们就可以通过三元表达式的形式,去精简这段功能语句。

其中三元表达式指的是三个元素,第一元素就是条件表达式、第二则是条件成立执行的语句,而第三则是条件不成立所执行的语句。

其语法格式为:

# 条件成立执行的语句  if 条件表达式 else  条件不成立执行的语句

根据上述代码,实现的形式如下:


x = 123
y = 456
res = print('x比y大') if x > y else print('y比x大')
print(res) # y比x大

列表生成式

Python为我们提供了在不失可读性的情况下,使代码更加精简,为我们提供了列表生成式、字典生成式等等。也可以叫做推导式。主要针对for循环以及可配合if语句的生成式。

将一个列表中,带有female数据存放到new_l列表中

平时的写法:

l = ['x1ong_male','liu_female','li_female','zhang_female','wang_male']


new_list = []

for info in l:
    if info.endswith('female'):
        new_list.append(info)

print(new_list)

列表推导式的写法:

new = [info for info in l if info.endswith('female')]
print(new)

其语法格式为

1、
# 条件成立执行的语句  for i in l if 条件表达式


2、如果推导式中不存在if条件式表达式,则默认为if True
   条件成立执行的语句  for i  in  l

列表推导式还可以for循环嵌套for循环,但前提是尽可能的不丧失代码的可读性。

字典生成式

其实字典生成式和列表生成式基本一致,会一个列表生成式,其他的集合生成式都会了。

# 字典生成式

keys = ['name','age','gender','addr']

dic = {k:None for k in keys} 
print(dic) # {'name': None, 'age': None, 'gender': None, 'addr': None}


# 使用字典生成式,将list_dic中的元组转换为字典中的键值对,忽略gender和其对应的值
list_dic = [('name','x1ong'),('age',17),('addr','china'),('gender','male')]

dic = {key:value for key,value in list_dic if key != 'gender'}
print(dic)  # {'name': 'x1ong', 'age': 17, 'addr': 'china'}

集合生成式

# 集合生成式


# 将set1列表使用集合生成式将其列表的元素转换为字典数据类型
list_set = ['x1ong',17,'male','china']

set1 = {i for i in list_set}
print(set1,type(set1)) # {'x1ong', 'male', 'china', 17} <class 'set'>

生成器生成式

生成式只针对容器类型,而元组则不是可变数据类型,所以没有元组生成式。那么我们使用(i for i in range(0,10)),会是一个什么样的生成式呢?

# 生成器生成式

list1 = ['1','x1ong','test','lulu','li','lxx']

g = (i for i in list1 )
print(g,type(g)) # <generator object <genexpr> at 0x7f9c2819b390>  <class 'generator'>

# 那么对于生成器,我们则需要使用next()方法将其值取出

while True:
	try:
		print(next(g))
	except StopIteration:
		break
本文作者: x1ong
免责声明:本博客所有文章仅用于学习交流
转载声明:文章为作者原创文章 转载请注明来源
本文链接: https://www.giaoblog.com/python/22276.html
上一篇
下一篇