python基础之生成器,生成器函数,列表推导式

内容梗概:
1. 生成器和生成器函数.
2. 列表推导式.

1.生成器函数
1.1 生成器函数. 就是把return换成yield
def gen():
    print("爽歪歪")
    yield "娃哈哈" # 可以让我们的函数分段运行
    print("酸酸乳")
    yield "AD钙奶"
    print("黄焖鸡米饭")

ret = gen() # 不会执行你的函数, 获取到生成器对象
# 迭代器不能等价代换
print(ret) # <generator object gen at 0x00000195518CFE60> generator 生成器
print(ret.__next__()) # 必须执行__next__()才可以触发生成器的运行 娃哈哈
print(ret.__next__()) # 必须执行__next__()才可以触发生成器的运行 AD钙奶
print(ret.__next__()) # StopIteration 迭代器, 就找yield, 执行不到才会报错
1.2 生成器函数的作用:节省内存,要多少用多少
def gen():
    lst = []
    for i in range(1, 10000):
        lst.append("衣服%s" % i)
        if i % 50 == 0: #  1 2 3 4 5 6 7 8 9 .. 49  50
            yield lst
            lst = [] # 每次生成一个新列表

g = gen() # 获取到生成器

print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

  

生成器和迭代器有相同的特点
# 节省内存
# 惰性机制
# 只能往前

生成器还可以使用__next__(), send()来访问生成器
send()可以给上一个yield位置传值
详见实例:
def func():
        print("水饺")
        a = yield "大馅水饺"
        print("a=", a)
        print("烧饼")
        b = yield "武大郎烧饼"
        print("b=",b)
        print("老婆饼")
        c = yield "只要老婆不要饼"
        print("c=", c)



1.3 x.send()的用法
gen = func() # 生成器
print("返回值是:", gen.__next__())
print("返回值是:",gen.send("混沌面")) #  和__next__()一样也是向下找yield. 给上一个yield传值
print("返回值是:",gen.send("胡辣汤")) #  和__next__()一样也是向下找yield. 给上一个yield传值
print("返回值是:",gen.send("马拉"))   # 和__next__()一样也是向下找yield. 给上一个yield传值
 send()不可以在第一个位置和最后一个位置出现
最后的yield后的代码是可以执行的但是会报错. StopIteration

注意:
生成器函数里不要写return
def  func():
            print("哈哈")
            yield  "呵呵"
            print("吼吼")
            return  "嘻嘻" # don't do this!

        gen = func()
        gen.__next__()
        gen.__next__()


2.推导式
2.1列表推导式:
lis = [结果 for循环 if判断语句]
实例1 筛选出名字长度小于二的
lst = ["中岛美雪", "夏川美里", "原由子", "汪峰", "田震","那英","周杰伦"]
new_lst = [ el for el in lst if len(el) == 2]  #此处快速创建了一个新列表,并用IF语句做出来筛选
print(new_lst)

  

实例2
[3,6,9]  已知 求转换成[(1,2,3), (4,5,6), (7,8,9)]

lst = [3,6,9]
new_lst = [(el-2, el-1, el) for el in lst]

  


2.2 字典推导式 {结果 for循环 if判断}
实例一
lst = [11, 22, 33]  #  => {0:11, 1:22, 2:33}
dic = {i:lst[i] for i in  range(len(lst)) }  #字典推导式
print(dic)
实例二
dic = {"张无忌":"赵敏", "杨过":"小龙女", "郭靖":"黄蓉"}
# 把字典中的key和value互换
# dic = {"赵敏":"张无忌", "小龙女":"杨过", "黄蓉":"郭靖"}
法1
dic1 = { v:k for k, v in dic.items()}  # 强化
print(dic1)
法2
dic2 = { dic[k]:k for k in dic} # 强化
print(dic2)

2.3 集合推导式 # 特点: 不可变. 不重复, 无序
{结果 for循环 if判断}
s = {i*"胡辣汤" for i in range(10)}
print(s)

注意: 没有元组推导式

2.1 生成器表达式 (结果 for循环 if判断) => 生成器表达式
2.1.1特点: 本质是迭代器. __next__()
1. 省内存
2. 惰性机制
3. 只能向前

2.1.2为啥省内存?
lst = ["衣服%s" % i for i in range(100000000)] # 占内存
print(lst)

g = ("衣服%s" % i for i in range(100000000)) # 生成器表达式. 节省内存,要多少多少
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

生成器推导式的一些坑
1. list()里面包含循坏,会循环生成器,打印成列表
2.生成器的惰性机制,拿一次取一次
详见实例:

实例1:
g = (i for i in range(10))
print(list(g)) # 把传递进来的数据转化成列表.  里面包含了for循环
list() => for=> __iter__() ==> __next__()
print(list(g)) # 上一次已经拿没了,因为生成器是一次性的,且只能往前取值,所以会打印出空表格
print(g.__next__())
for el in g:
    print(el)
for el in g: # 已经没有数据了 惰性机制-> 只能往前
    print(el)

实例2 :
def func(): # 生成器函数
    print(111)
    yield 222
g = func()          #  生成器 -> 没有执行过__next__()
g1 = (i for i in g)  # 生成器表达式. 也没有__Next__()
g2 = (i for i in g1) # 生成器表达式. 也没有__Next__()
# 到此为止, 没有人拿过值
print(list(g))  #  111 [222]
print(list(g1)) #  []   #因为前面的g已将拿过值了,g已经空了,先到先得
print(list(g2)) # []


tips:生成器表达式和列列表推导式的区别:
1. 列表推导式比较耗内存. 一次性加载. 生成器表达式几乎不占⽤用内存. 使用的时候才分配和使用内存
2. 得到的值不一样. 列表推导式得到的是⼀个列列表. 生成器表达式获取的是⼀个生成器.

补充知识点:yield from
在python3中提供了了一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回
def gen():
    lst1 = ["⿇花藤", "胡辣汤", "微星牌饼铛", "Mac牌锅铲"]
    lst2 = ["饼铛还是微星的好", "联想不能煮鸡蛋", "微星就可以", "还可以烙饼"]
    yield from lst1
    yield from lst2
g = gen()
for el in g:
    print(el)   #结果会先将lst1先打印完,然后再打印lst2
原文地址:https://www.cnblogs.com/Mixtea/p/9892265.html