Python的生成器

一 生成器与yield

  若函数体包含yield关键字,再调用函数,并不会执行函数体代码,得到的返回值即生成器对象。

>>> def my_range(start,stop,step=1):
...     print('start...')
...     while start < stop:
...         yield start 
...         start+=step
...     print('end...')
... 
>>> g=my_range(0,3)
>>> g
<generator object my_range at 0x104105678>

  生成器内置有__iter__和__next__方法,所以生成器本身就是一个迭代器。

>>> g.__iter__
<method-wrapper '__iter__' of generator object at 0x1037d2af0>
>>> g.__next__
<method-wrapper '__next__' of generator object at 0x1037d2af0>

  因而我们可以用next(生成器)触发生成器所对应函数的执行,

>>> next(g) # 触发函数执行直到遇到yield则停止,将yield后的值返回,并在当前位置挂起函数
start...
0
>>> next(g) # 再次调用next(g),函数从上次暂停的位置继续执行,直到重新遇到yield...
1
>>> next(g) # 周而复始...
2
>>> next(g) # 触发函数执行没有遇到yield则无值返回,即取值完毕抛出异常结束迭代
end...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

  既然生成器对象属于迭代器,那么必然可以使用for循环迭代,如下:

>>> for i in countdown(3):
...     print(i)
... 
countdown start
3
2
1
Done!

  有了yield关键字,我们就有了一种自定义迭代器的实现方式。yield可以用于返回值,但不同于return,函数一旦遇到return就结束了,而yield可以保存函数的运行状态挂起函数,用来返回多次值。

  总结:return和yield的区别?
  (1)return:只能返回一次值,就结束函数。
  (2)yield:可以返回多次值。
  (3)yield:可以将函数暂停到某个位置。
  (4)return和yield不能混用。

二 yield表达式应用

# 如何得到自定义的迭代器?
# (1)在函数内存在yield关键字.
# (2)调用函数并不会执行函数体代码.
# (3)会返回一个生成器对象,生成器即自定义的迭代器
def func():
    # return  # 返回函数内的值,但是只能返回一个值,程序遇到return就会跳出.
    print("第一次")
    yield 1
    print("第二次")
    yield 2
    print("第三次")
    yield 3
    print("第四次")

g = func()
print(g)
# 运行结果是: <generator object func at 0x0000025A10E4F660>  generator翻译:生成器,其实生成器就是迭代器,只不过是自定义出来的.

# 如何使用这个自定义的生成器?
# g.__iter__()
# print(g.__iter__())

# 会触发函数体代码的运行,然后遇到yield停下来.将yield后面的值当作本次调用的结果返回
# g.__next__()
print(g.__next__())  # 第一次 1
print(g.__next__())  # 第二次 2
print(g.__next__())  # 第三次 3
print(g.__next__())  # 没有返回值之后报错 :StopIteration
# 统计字符串长度
print("aaa".__len__())
len("aaa")  # 本质是"aaa".__len__()

next(g)  # 本质是g.__next__()
# iter(可迭代对象)  本质可迭代对象.__iter__()

 yield表达式的应用(一)

# 简单的理解yield
def func():
    print("start....")
    x = yield 111  #
    print("哈哈哈哈")
    yield 222
    # print("stop....")

g = func()
res = g.send(None)  # 必须先运行一下g.send(None)等同于next(g)
print(res)  # 返回值是 111

res = g.send("lsj")  #
print(res)  # 返回值是 222
"""
start....
111
哈哈哈哈
2222
"""

 

# yield后不跟返回值
#
x = yield返回值(默认返回值为None)一定在函数内的生成器 # 把生成器当作一条狗喂狗投食 def dog(name): print("道哥%s准备吃东西了..."%name) while True: # x = yield None # 不写返回值表示返回值是None # x拿到的是yield接受到的值 x = yield # 不写返回值表示返回值是None print("道哥%s吃了%s"%(name,x)) g = dog("alex") # print(g) # <generator object dog at 0x0000021DC24AF430> # print(next(g)) # 道哥lsj准备吃东西了 None # res = next(g) # print(res) # None # next(g) # 道哥alex吃了None,遇到yield停下来等待 # 使用send为x传值:也就是seed为yield赋值,yield在传给x # g.send("一根骨头") # TypeError: can't send non-None value to a just-started generator g.send(None) # 等同于next(g) # 测试g.send(None)等同于next(g) # g.send(None) # 道哥alex吃了None # next(g) # 道哥alex吃了None # 再次传值可以是字符串、可以是类型的数据 g.send("一根骨头") # 道哥alex吃了一根骨头 g.send("肉包子") # 道哥alex吃了肉包子 g.send([1,2,3]) # 道哥alex吃了[1, 2, 3] # 总结有yield和没有yield的区别? # (1)有yield的生成器,运行到yield可以暂停。等待传值后在运行。 # (2)没有yield的函数在运行时,不停止一直运行。 # 关闭g # g.close() # g.send("1111") # 关闭后再次传值会报错StopIteration

 yield表达式的应用(二)

# yield表达式的应用(二)了解
# x = yield 返回值(默认返回值为None)一定在函数内的生成器
# 把生成器当作一条狗喂狗投食
def dog(name):
    food_list = []  # 定义一个空列表
    print("道哥%s准备吃东西了..."%name)
    while True:
        # x = yield None # 不写返回值表示返回值是None
        # x拿到的是yield接受到的值(值先给yield后给x)
        # x = yield 1111 # yield后跟返回值:1111先赋值给yield后把yield赋值给x
        x = yield food_list # yield后跟空列表
        print("道哥%s吃了%s"%(name,x))
        food_list.append(x)

g = dog("alex")
# g.send(None)  # 道哥alex准备吃东西了...
# res = g.send(None)  # 道哥alex准备吃东西了...
# print(res)  # 1111

# 加入send功能x
# res = g.send("肉包子")
# print(res)
#
# res = g.send(["a","b"])
# print(res)

# 向定义的列表内传参
res = g.send(None)
print(res)  # []

res = g.send("一根骨头")
print(res)  # ['一根骨头']

res = g.send("肉包子")
print(res)  # ['一根骨头', '肉包子']

三 三元表达式、列表生成式、生成器表达式

  • 三元表达式
# 需求: 比较两个参数的大小,返回较大值
# def func(x,y):
#     if x > y:
#         return x
#     else:
#         return y
#
# res = func(1,2)
# print(res)

# 使用三元表达式,解决上面的需求
# 语法格式:条件成立时返回的值 if 条件 else 条件不成立时返回的值
x = 1
y = 2
res = x if x > y else y 
print(res)
  • 列表生成式
  • 列表生成式是python为我们提供的一种简化代码的解决方案,用来快速生成列表,语法如下
[expression for item1 in iterable1 if condition1
for item2 in iterable2 if condition2
...
for itemN in iterableN if conditionN
]
l = ['lsj','lxx_dsb','alex_dsb','wxx','xxq_dsb']
# 需求:将含有dsb放到新列表里
# new_l = []
# for name in l:
#     if name.endswith('dsb'):
#          new_l.append(name)
# print(new_l)

# 列表生成器的目的是将上述代码更精简
# new_l = [name for name in l if name.endswith('dsb')]  # ['lxx_dsb', 'alex_dsb', 'xxq_dsb']
# new_l = [111 for name in l if name.endswith('dsb ')]  # [11, 11, 11]
new_l = [name for name in l ]  # ['lsj', 'lxx_dsb', 'alex_dsb', 'wxx', 'xxq_dsb']
print(new_l)
# 案例
# 把所有小写字母全变成大写
new_l = [name.upper() for name in l ]
print(new_l)
# 把所有的名字去掉后缀_dsb
# new_l = [name.split('_')[0] for name in l ]  # 方式一
new_l = [name.replace('_dsb','') for name in l ]  # 方式二
print(new_l)
  • 生成器表达式
l = [表达式 for x in 可迭代对象 if 条件]
g = (表达式 for x in 可迭代对象 if 条件)  next(g)
sum(表达式 for x in 可迭代对象 if 条件)
list(表达式 for x in 可迭代对象 if 条件)
dic = {键:值 for k in 可迭代对象 if 条件}
set = {元素 k in 可迭代对象 if 条件}
# (1)字典生成式
keys = {'name','age','gender'}
dic = {key:None for key in keys}
print(dic,type(dic))  # {'age': None, 'gender': None, 'name': None} <class 'dict'>

# 把下面的列表转换成字典,并且不要gender的数据
items =[('name','lsj'),('age',18),('gender','male')]
dic = {k:v for k,v in items if k != 'gender'}
print(dic,type(dic))  # {'name': 'lsj', 'age': 18} <class 'dict'>

# (2)集合生成式
set = ['name','age','gender']
set1 = {k for k in set}
print(set1,type(set1))  # {'age', 'name', 'gender'} <class 'set'>

# (3)元组生成式,从0到10一组数
g = (i for i in range(10) if i > 3)
print(g,type(g))  # 得到不是元组(因为元组式不可变数据类型),所以没有元组生成式,这是一个生成器表达式.
                #  <generator object <genexpr> at 0x0000022B79C4F7B0> <class 'generator'>

# (4)针对生成器的玩法?
ge = (i for i in range(10) if i > 3)
# 强调:此刻ge内部一个值也没有,没等到next(ge)的时候取出一个值,再next(ge)的时候再取出一个值
print(ge)
print(next(ge))  # 4
print(next(ge))  # 5
# 应用:统计笔记.txt文件里的字符数 sum()

# print(sum([1,2,3,4,5]))  # sum()对传入的可迭代对象进行累加,运行结果是:15
# 方式一:
# with open('笔记.txt',mode='rt',encoding='utf-8') as f:
#     res = 0
#     for line in f:
#         res += len(line)
#     print(res)

# 方式二:如果文件行过多,此时文件内容放入f的数据太多
# with open('笔记.txt',mode='rt',encoding='utf-8') as f:
#     size_of_line = [len(line) for line in f]
#     print(size_of_line)  # 每一行的长度 [11, 1]
#     res = sum(size_of_line)
#     print(res)

# 方式三:效率最高,不受方式二文件行数过多影响
# with open('笔记.txt',mode='rt',encoding='utf-8') as f:
#     g = (len(line) for line in f)
#     print(g)
#     res = sum(g)
#     print(res)
# 方式三:优化
with open('笔记.txt',mode='rt',encoding='utf-8') as f:
    # res = sum((len(line) for line in f)) # 运行结果: 10 
    res = sum(len(line) for line in f)  # 同上的效果  10
    print(res)

  

原文地址:https://www.cnblogs.com/liunaixu/p/12657021.html