一、生成器
生成器,就是自己用代码写的迭代器,生成器的本质就迭代器。
之前的,将一个可迭代对象 转化 成为迭代器。
li = [1,2,3,5,18] # 这是一个列表 可迭代对象
iter1 = li.__iter__() # 将一个迭代对象 转换成 迭代器
iter1 = iter(li) # 这个是内置函数,本质上还是调用对象的__iter__()方
x = 15
iter(x) # TypeError: 'int' object is not iterable
上面的li 是python源码提供的基本数据类型之一 列表,而 __iter__() 或者 iter()都是源码提供的,不是我们自己写的
用以下两种方式构建一个生成器:
1,通过生成器函数
2,生成器表达式
生成器函数:
# 生成器函数 # 首先来看一看,普通函数的定义 def func1(x): x += 1 print(x) return x result = func1(10) # 对于普通函数,函数名()就是调用或执行函数,将会执行函数体的代码 print(result) # 生成器函数 def generator_func1(x): x += 1 print(333) yield x x += 3 print(444) yield x g_obj = generator_func1(10) # 调用或执行生成器函数,并不会执行函数体,而是在内存中创建一个生成器函数对象 # print(g_obj) # <generator object generator_func1 at 0x00000145FDCD0E60> # 问题来了?那么如何 执行 生成器函数对象 对应的 函数的函数体呢? 答案,调用生成器函数对象的__netx__()方法,即生成器的本质就是迭代器 v1 = g_obj.__next__() # 11 当第一次调用生成器对象的__next__()方法时,从对应的生成器函数开头,从上往下 向下执行,当遇到第一个yield,返回yield后面的 # 值 ,函数挂起。 print(v1) # 11 v2 = g_obj.__next__() # 14 # 从函数上次执行到的yield语句 下 的第一条语句,继续向下执行,下到遇到yield语句,返回yield后面的值 print(v2) v3 = g_obj.__next__() # v3 = g_obj.__next__() StopIteration 当取值都取完了,再取就报错,停止迭代 # 总结 : 一个__next__() 或者 next(g_obj) 方法 对应一个 yield # yield 将其后面的值,如果是一个数据,原封不动返回,如果多个值 ,放在元组里返回,将值返回给 生成器对象.__next__()方法 # 生成器函数 yield 与 普通函数 return 的区别 # return 结束函数,给函数的执行者返回值 # yield 不会结束函数,一个next 对应 一个 yield , 给生成器对象.__next__() 返回值 。
生成器函数 vs 迭代器:
# 生成器函数 VS 迭代器 # 区别1: 自定制的区别 l1 = [1,2,3,4,5] # 比较死板,不灵活 iter1 = iter(l1) def func1(x): x += 1 yield x x += 3 yield x x += 5 yield x g1 = func1(5) # 国为生成器,是通过生成器函数 创建的, 函数可以传参,就显得比较灵活 print(g1.__next__()) print(g1.__next__()) print(g1.__next__()) # 区别2 : 内存级别的区别 # 迭代器需要通过可迭代对象进行转化。 可迭代对象非常占用内存 # 生成器直接创建,不需要转化,从本质上就节省内存。 def func1(): for i in range(1,100000): yield i g2 = func1() for i in range(50): print(g2.__next__()) count = 1 while count < 51: print(g2.__next__()) count += 1 # send 与 __next__()区别: def func10(): print(111) count = yield 6 print(222) print(count) count1 = yield 7 print(count1) yield 8 g10 = func10() print(g10.__next__()) # 111 6 print(g10.send("send1")) # 222 send1 7 print(g10.send("send2")) # send2 8 # send 与 next()方法,或者 生成器对象.__next__()方法一样,也是对生成器取值的方法。取值就是执行一个yield # send 可以上一个yield 整个表达式传值 ,如果yield 左边有变量接受,则赋值给它。 # 第一次取值 永远都是next # 最后一个yield 永远也得不到send传的值。 def cloth(n): for i in range(1,n+1): yield "第%d件衣服成功产出" % i g_cloth = cloth(1000) # 创建了一个可以产生1000件衣服的生成器,但是是一件一件地产出的 # print(g_cloth.__next__()) # print(g_cloth.__next__()) # 下指令,生产50件衣服 # for i in range(50): # print(g_cloth.__next__()) def cloth2(n): for i in range(1,n+1): yield "第%d件衣服成功产出" % i,"共消耗成本{}元".format(i * 85) g_cloth2 = cloth2(10000) count = 1 while count < 51: print(",".join(list(next(g_cloth2)))) # print(next(g_cloth2)) count += 1
def cloth2(n):
for i in range(n+1):
yield "衣服 %s" % i
generator2 = cloth2(1000)
for num in range(51): # range(5) ,也是一个迭代对象,数字范围太大,也占用内存间
print(generator2.__next__()) # 调用生成器函数对象的__next__() 或者 send() 方法,将推动 对应的生成器函数执行,
# 执行到,遇到下一个yield 语句,函数挂起,下次调用__next__() 或者 send() 方法,上一个yield
# 语句之后,继续 执行,直到下一个yield语句,依次类推。
count = 1
while count < 51: # 继续生产50件
print(next(generator2))
count += 1
def fuc_gene():
x = 5
print(x)
yield x,10
x = {'name':"chris",'age':18}
print(x)
yield x,[1,2,3] # 返回时,多个值放到一个元组中,和return一样
gene = fuc_gene() # 直接调用或执行生成器函数,并不会执行其函数体,而是在内存中创建一个生成器函数对象
print(gene.__next__()) # 通过调用生成器函数对象的__next__() 或者 send()方法来,来推动与之对应的生成器函数的陈函数体的执行。
print(gene.__next__())
# 列表推导模式:一行代码几乎搞定你需要的任何的列表
# 循环模式 [变量(加工后的变量) for 变量 in iterable ]
l = [i for i in range(1,101)]
print(l)
l2 = ["开学第%d几天" % i for i in range(1,101)]
print(l2)
l3 = [i*i for i in range(1,11)]
print(l3)
# 筛选模式 [ 变量 (加工后的变量) for 变量 in iterable if 条件 ]
l4 = [i for i in range(1,31) if i % 2 == 0]
print(l4)
l5 = [i**2 for i in range(1,31) if i % 3 == 0]
print(l5)
names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
name = [name for list1 in names for name in list1 if name.count("e") == 2]
print(names)
print([j for i in names for j in i if j.count("e") == 2])
print(name)
# 列表推导式
# 优点:一行解决,方便。
# 缺点:容易着迷,不是易排错,不能过超过三次循环
# 列表推导式不能解决所有列表的问题,所以不要太刻意用。
# 生成器表达式:将列表推导式的 [] 转换成 () 即可
g = (i for i in range(1,11)) # 得到一个生成器
print(g) # <generator object <genexpr> at 0x000001D0C61922B0>
# print(g.__next__())
# while True:
# try:
# print(next(g))
# except StopIteration:
# break
# li_test = [print(5) for i in range(5)] # 打印 5 次 5
# print(li_test) # [None, None, None, None, None]
#
test2 = [print(10) for i in range(5)] # 遍历 for 循环中的 可迭代对象,每取一个值 ,执行for循环前面的语句,将值 追加到列表当中,
# print()没有返回值 ,所以是None, ...注意:列表推导式,for 前面只能一条语句,多条的话,就
# 要考虑用函数来封装,且执行列表推导式时,函数执行
def tell(n):
print("追加第%d个元素到当前列表中" % n)
return n
test3 = [tell(i) for i in range(1,6)]
'''
输出结果:
追加第1个元素到当前列表中
追加第2个元素到当前列表中
追加第3个元素到当前列表中
追加第4个元素到当前列表中
追加第5个元素到当前列表中
'''
print(test3) # [1, 2, 3, 4, 5]
# 生成器表达式扩展
sum2 = 0
def sum1(n): # 刚才犯一个错误,将全局变量和函数名,取名一样,我们说函数名就是特殊的变量,不能重名,否则执行时会出现意想不到的错误
global sum2
sum2 += n
return sum2
# print(sum2)
g1 = (sum1(i) for i in range(1,11)) # 列表生成器表式式,会将普通函数的 return 语句 转换成 yield 语句
print(g1)
print(sum2)
print(g1.__next__())
print(g1.__next__())
print(g1.__next__())
print("-*"*30)
# 我的猜想,然后,再根据 for 循环的次数变成生成器函数:
sum2 = 0
def g1_func(): #
start = 1
while start < 11: # 这样,就节省内存空间,不需要事先创建可迭代对象
global sum2
sum2 += start
start += 1
yield sum2
g2 = g1_func()
print(g2.__next__())
print(g2.__next__())
print(g2.__next__())
# 字典推导式:
mcase = {'a':10,'b':34}
mcase_frequency = {mcase[k]: k for k in mcase}
print(mcase_frequency)
mcase.get('a')
# 其它推导式:
# 合并大小写对应的value值,将k统一成小写
mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
# mcase_frequency = {k.lower(): mcase.get(k.lower(),0) + mcase.get(k.upper(), 0) for k in mcase.keys()}
# print(mcase_frequency)
# dict.get(key, default=None)
print(mcase.get('b',1))
print(reversed([1,3,5]))
# 求(x,y)其中x是0-5之间的偶数,y是0-5之间的奇数组成的元祖列表
li2 = [(x,y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1]
print(li2)
li2_iter = ((x,y) for x in range(6) if x % 2 == 0 for y in range(6) if y % 2 == 1)
print(li2_iter) #<generator object <genexpr> at 0x000001F4D2FDC200>
for i in li2_iter: # 迭代器,也可以for 循环
print(i)
# def g_function(): # print(555) # ret1 = yield {1,3} # print(666) # g = g_function() # print(g.__next__()) # print(g.__next__()) # 一个 next 对应一个 yield , 生成器函数 ,只有一个yield 所以报错 def g_function(): print(555) ret1 = yield {1,3} print(ret1) print(666) yield "success" g = g_function() print(g.__next__()) # print(g.__next__()) # None 666 success print(g.send("hello world")) # 上一个yield 表达式,传值 ,赋值 给 左边的变量 # hello world 666 success
生成器函数 与 普通函数的区别:
def fuc_gene(): x = 5 print(x) yield x,10 x = {'name':"chris",'age':18} print(x) yield x,[1,2,3] # 返回时,多个值放到一个元组中,和return一样 gene = fuc_gene() # 直接调用或执行生成器函数,并不会执行其函数体,而是在内存中创建一个生成器函数对象 print(gene.__next__()) # 通过调用生成器函数对象的__next__() 或者 send()方法来,来推动与之对应的生成器函数的陈函数体的执行。 print(gene.__next__())
列表推导式一些例子:
有一个列表 :l1 = ['alex', 'WuSir', '老男孩', '太白']将其构造成这种列表['alex0', 'WuSir1', '老男孩2', '太白3']
l1 = ['alex','WuSir','老男孩','大白3']
dest4 = [e+str(index) for index,e in enumerate(l1)]
print(dest4)