生成器 补充

主要内容

  • 生成器
  • 生成器函数
  • 列表推导式
 什么是生成器: 本质就是迭代器
在python中有三种方式来获取生成器:
1 通过生成器函数
2通过各种推导式来实现生成器
3通过数据的转换也可以以(获取)生成器
例子1
def func():
    print(111)
    return 22
a=func() # 111
print(a) #222  #
 生成器函数

retuurn和yield的关系:

    return是用来返回具体的某个值,yield一般与循环一起用,相当于生成了一个容器(常见的就是字典),
    然后在这个容器里面存放了每次循环以后的值,并且就在那放着,不输出,不返回,
    等你下次需要他的时候直接取出来用(调用)就行

yield和__next__()的关系:

    yield关键字的特点: 可以记录当前函数中执行的位置,下一次继续执行
    next和yield是一对搭档 : next开始函数的执行 yield停止函数的执行

例子2
def func():
    print(111)
    yield 222
ret=func()
print(type(ret)) #<class 'generator'>   generator:生成器
print(ret) # <generator object func at 0x00000148B4751D58>

运行结果和上面不一样 由于函数中存在了yield,那么此时的函数就是一个生成器函数
这个时候 再用函数的调用方法就不行了,此时该函数不执行,而是获得一个生成器
想要执行此函数就可以直接__next__()来执行,因为生成器的本质就是迭代器.
def func():
    print(111)
    yield 222# 函数遇到yiled会暂时停止和retun的区别在于 yiled是分段执行函数 return是直接停止函数

ret=func()  # 函数此时不执行,而是返回一个生成器 (内部有__iter__()方法),执行就跟迭代器一样了
print(type(ret))  查看是什么类型 结果为 generator
print(dir(ret))  # 查看内部方法
a=ret.__next__()  # 执行方式:迭代器 点__next__() 可以赋值  遇到这才开始进入函数 自上而下运行 
            不过如果你是一个yield就暂停到这 print(a) print(ret.__next__()) # 此时函数报错,是因为只有一个yiled 可以理解为一个__next__()执行一个yield, __next__() 本来就是一个一个拿 而恰巧遇到yield 暂停函数(分段执行)
生成器的一个简单作用:  (例子:订衣服)

一般函数解决:
def cloth():
    lst=[]
    for i in range(0,10000):
        lst.append('衣服'+str(i))
    return lst

a=cloth()
print(a)  # 一次全拿出来 放到列表中
不完美 目的是我想要几个你给我几个(这样写太占内存,不好)
所以:
def cloth1():
    for i in range(0,10000):
        yield  '衣服'+str(i)   #  惰性机制  内部自己执行自加一,不会全部加载出来

g=cloth1()  # 返回一个生成器

print(cloth1().__next__()) # 迭代器 点 __next__() 执行

for i in range(10):  #  取十件

    print(g.__next__())  # ⽣成器是⼀个⼀个的指向下⼀个. 不会回去, __next__()到哪, 指针就指到哪⼉.
                          下⼀次继续获取指针指向的值.
print(g.__next__()) # 从第十个数开始 我又取了一件

for i in g:  #  g他本身也是一个生成器 ,内部含有__iter__() 所以可迭代    内部__next__()不冲突
    print(i)

send 方法  和__next__()一样 都可以让生成器执行到下一个yield
def func():

    print('今天吃的什么饭')

    a=yield  '馒头'
    print(a)
    yield '狗粮'
a=func()

ret=a.__next__()
print(ret)
ret1=a.send('胡辣汤')  # 相当于 __next__()
print(ret1)
send和__next__()都是让程序向下走一次

send可以让yield的位置传递参数,不能给最后一个yield发送值 会报错,第一次执行函数是不能用send,就是先用__next__()开下头
for循环可以直接循环生成器来获取内部元素
def func():
     print(111)
     yield 222
     print(333)
     yield 444
     print(555)
     yield 666
gen = func()
for i in gen:
 print(i)
结果:
111
222
333
444
555
666
 列表推导式,⽣成器表达式以及其他推导式
列表推导式是通过一行来构建你想要的列表,常用写法,[结果 for 变量 in可迭代对象]
lst = [i for i in range(1, 15)]
print(lst)
# 还可以进行筛选:
# 获取1-100所有的偶数
print([i for i in range(1,101) if i%2==0])
 
生成器表达式 和 列表表达式一样,只是把[]替换成()
例子:
ss=(i for i in range(10))
print(ss) #generator  直接打印出来就是一个生成器
我们可以用for循环来循环这个生成器:
sw=('第%s次打你'% i for i in range(10))
for i in range(2):
    s=sw.__next__()
    print(s) #  输出0和1 两次 因为取了两个数
for i in sw:
    print(i) # 全部打印出来,for循环内部有__next__()的东西,还会帮你处理异常(报错)
生成器表达式也可以进行筛选

    例子:
ss=(i*i for i in range(100) if i%3==0)

for i in ss:
    print(i) # 打印出100以内所有能被3整除的数的平方

例子: 寻找名字中带有两个e得人的名字
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven',
'Joe'],
 ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
# 列表推导式写法:
gen = (name for first in names for name in first if name.count("e") >= 2)
for name in gen:
 print(name)

正常代码写法:
lst=[]
for name in names:
for na in name:
if na.count('e')>=2:
lst.append(na)
print(lst)
 

小结:

⽣成器表达式和列表推导式的区别:

1. 列表推导式比较耗内存. ⼀次性加载. ⽣成器表达式⼏乎不占⽤内存. 使⽤的时候才分
配和使⽤内存
2. 得到的值不⼀样. 列表推导式得到的是⼀个列表. ⽣成器表达式获取的是⼀个⽣成器.
举个栗⼦.
同样⼀篮⼦鸡蛋. 列表推导式: 直接拿到⼀篮⼦鸡蛋. ⽣成器表达式: 拿到⼀个老⺟鸡. 需要
鸡蛋就给你下鸡蛋.
⽣成器的惰性机制: ⽣成器只有在访问的时候才取值. 说⽩了. 你找他要他才给你值. 不找他
要. 他是不会执⾏的.
补充:
def func():
    print(111)
    yield 222
g=func() # 生成器


g1=(i for i in g)
# print(g1.__next__())

print(list(g))  # 通过数据转换可以获取生成器相当于print (g.__next__())

执行顺序 当你取值的时候函数才会被执行, 而且你只有一个__next__()取一次少一个
总结:

  
推导式有, 列表推导式, 字典推导式, 集合推导式, 没有元组推导式
⽣成器表达式: (结果 for 变量 in 可迭代对象 if 条件筛选)
⽣成器表达式可以直接获取到⽣成器对象. ⽣成器对象可以直接进⾏for循环. ⽣成器具有
惰性机制.
目前知道了 获取生成器的几种方式:              
         直接生成器点__next__()

for 循环(目的也是加了__next__())
    通过数据转换可以,不过他会把取得值放到你所转的类型中去
 






原文地址:https://www.cnblogs.com/systemsystem/p/9919763.html