18.Python基础篇-迭代器、生成器、推导式
函数进阶-迭代器
双下方法:
很少直接调用,一般情况下,都是通过其他语法触发的(Python解释器调用的方法)
可迭代协议 与 迭代器协议
可迭代的iterable与迭代器iter
可迭代协议:含有__iter__方法的都是可迭代的。
可迭代的,一定可以被for循环。只要含有__iter__()方法能被for循环。
迭代器协议:含有__iter__方法和__next__方法的。
迭代器一定可迭代。
可迭代的通过iter()方法就能得到一个迭代器。
迭代器的特点:
1.惰性计算:迭代器只在需要时生成数据,适合处理大数据集,减少内存占用。
2.随着循环每次在内存中生成一个值,而不是全部。每次调用next时返回一个值,也不是全部。
list1 = [1, 2, 3] # 只有__iter__方法,没有__next__方法,说明列表是可迭代的 print('__iter__' in list1.__dir__()) print('__next__' in list1.__dir__()) # 输出结果 # True # False # 通过iter()方法将列表转换为迭代器。并验证是否存在__iter__方法与__next__方法。 list1_iterator = iter(list1) # 调用iter方法返回当前可迭代对象的迭代器 print('__iter__' in list1_iterator.__dir__()) print('__next__' in list1_iterator.__dir__()) # 输出结果: # True # True # 一个类中只要有iter方法就会认为是可迭代的,如果既有iter方法还有next方法,就会认为是迭代器 from collections import Iterator from collections import Iterable class A: def __iter__(self): pass def __next__(self): pass a = A() print(isinstance(a, Iterator)) # 判断是否是迭代器 print(isinstance(a, Iterable)) # 判断是否是可迭代的 # 输出结果: # True # True # 迭代器中的__next__()方法 # __next__():以0为起点,获取迭代器中的下一个值 print(list1_iterator.__next__()) # list1_iterator中的第一个值 print(list1_iterator.__next__()) # list1_iterator中的第二个值 # 输出结果 # 1 # 2 # 迭代器中的__iter__()方法 iterator = list1.__iter__() # 返回一个当前可迭代对象的迭代器 print(iterator) # 输出结果: # <list_iterator object at 0x00000271EBB2B970> # 迭代器能节省内存的演示 print(range(1, 100000000000)) # 很快生成。因为range是一个迭代器,一个一个的返值 print(list(range(1, 100000000000))) # 会报错MemoryError,电脑内存不够容纳这么大的列表。因为list不是迭代器,会一次生成全部数据。
函数进阶-生成器
生成器的本质,就是自己写的迭代器。一次返回一个值,而不是一次性生成所有的值。生成器通过使用 yield
关键字定义。(有yield的函数就是生成器函数)
生成器的两种形式:1.生成器函数。2,生成器表达式
生成器函数
生成器函数的定义:含有yield关键字的函数就是生成器函数;
生成器函数的特点:
1.调用函数时不会执行函数内部代码,而是返回一个生成器内存地址。
2.每次调用next()方法的时候会取到一个值。
3.直到取完最后一个,再执行next时会报错。
yield关键词
yield与return不能共用。与 return
不同,yield
不会终止函数的执行,而是暂停函数的执行,并返回一个值给调用者。函数在下次调用时会从上次暂停的地方继续执行。
def simple_generator(): yield 1 yield 2 yield 3 gen = simple_generator() print(next(gen)) # 输出: 1 print(next(gen)) # 输出: 2 print(next(gen)) # 输出: 3 # print(next(gen)) # 抛出 StopIteration 异常
生成器函数代码演示:
def generator(): print('生成器函数中打印的数据1') yield 1 print('生成器函数中打印的数据2') yield 2 print('生成器函数中打印的数据3') yield 3 g = generator() # 返回一个生成器 print(g) # 结果:<generator object generator at 0x103141540> print(g.__next__()) # 通过next取值
生成器函数取值的三种方式:
1.通过__next__()取值:上面的演示代码中已经演示
2.通过for循环取值:
def generator(): print('生成器函数中打印的数据1') yield 1 print('生成器函数中打印的数据2') yield 2 print('生成器函数中打印的数据3') yield 3 g = generator() # 返回一个生成器 for i in g: print(i) 执行结果: 生成器函数中打印的数据1 1 生成器函数中打印的数据2 2 生成器函数中打印的数据3 3
3.通过数据类型转换从生成器中取值(比较占用内存):
def generator(): print('生成器函数中打印的数据1') yield 1 print('生成器函数中打印的数据2') yield 2 print('生成器函数中打印的数据3') yield 3 g = generator() print(list(g)) # 执行结果: 生成器函数中打印的数据1 生成器函数中打印的数据2 生成器函数中打印的数据3 [1, 2, 3]
生成器函数与普通函数的区别:
1. 定义方式
- 普通函数:使用
return
关键字返回一个值,函数执行完毕后结束。 - 生成器函数:使用
yield
关键字逐个返回值,函数执行可暂停并保持状态。
2. 返回值
- 普通函数:返回单一值,并终止函数执行。
- 生成器函数:返回一个生成器对象,可以多次调用,逐个生成多个值。
3. 内存管理
- 普通函数:一次性生成所有返回值,可能导致高内存占用。
- 生成器函数:按需生成值,内存占用小,适合处理大数据。
4. 调用方式
- 普通函数:调用后不可再用,必须重新调用。
- 生成器函数:可以通过
next()
逐步获取值,也可以在for
循环中迭代。
5. 状态保持
- 普通函数:无法保持执行状态,调用后状态丢失。
- 生成器函数:保持状态,每次调用继续上次暂停的位置。
总结
- 生成器函数通过
yield
提供了惰性求值的能力,适合需要高效内存管理和逐步生成数据的场景。
生成器函数进阶
send()
send():用于恢复生成器的执行,同时向生成器传递一个值。生成器在遇到 yield
时暂停执行,通过 send()
可以将一个值发送给生成器中断点处的 yield
表达式,并继续执行生成器直到遇到下一个 yield
。
def generator(): print('A') parameter = yield 1 print(parameter) print('B') yield 2 g = generator() print(g.send(None)) # 执行printA,打印返回值1 print(g.send("传递的参数aaaa")) # 执行print(parameter),执行print(B),打印返回值1
send与next的区别:send在获取下一个值的效果和next基本一致。只是send在获取下一个值的时候,给上一个yield的位置传递一个参数。
send使用注意事项:
第一次调用send时必须send(None)
最后一个yield不能接受外部的值
yield from语法
# yield from说明 def generator(): s1 = 'abcdefg' s2 = '1234567' for i in s1: yield i for i in s2: yield i g = generator() for i in g: print(i) # 上面这个功能使用yield from实现。实现效果一样,可以节省部分代码量。 def generator(): s1 = 'abcdefg' s2 = '1234567' yield from s1 + s2 g = generator() for i in g: print(i)
预激活生成器的装饰器
def init(func): def inner(*args, **kwargs): g = func() g.__next__() return g return inner @init def avg_g(): sum = 0 count = 0 avg = 0 while True: num = yield avg sum += num count += 1 avg = sum / count g = avg_g() print(g.send(10)) print(g.send(20))
推导式和表达式
列表推导式
语法演示:
# 列表推导式 list1 = [i for i in range(10)] # 将range(10)里面的每个结果,存到列表中的每个元素中 print(list1) # 输出结果:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 了解即可 names = [['Tom','Billy','Jefferson', 'Steven','Joe','Andrew','Wesley'], ['Alice','Jill','Ana','Wendy','Jennifer','Sherry','Eva']] list1 = [name for lst in names for name in lst if name.count('e')==2] # 列表推导式中可以嵌套for循环可以写到一行 print(list1)
生成器表达式
# 生成器表达式 g = (i for i in range(10)) print(g) # <generator object <genexpr> at 0x00000249D1AB8CF0> for i in g: print(i) # 表达式增加if判断,这个操作也同样适用于列表推导式 g = (print(i) for i in range(10) if i%2==1) # if条件满足,才执行print(i) print(g) # <generator object <genexpr> at 0x00000249D1AB8CF0> for i in g: pass
列表推导式与生成器表达式的区别:
2.返回的值不一样。一个是完整的列表,生成器表达式返回一个生成器
3.生成器表达式比列表推导式更节省内存。
字典推导式
# 案例:将key和value调换{10: 'a', 20: 'b'}这样 mase = {'a': 10, 'b': 20} mase = {mase[key]: key for key in mase} print(mase)
集合推导式
# 集合推导式,自带结果去重功能 squared = {i for i in [1, 1, 2]} print(squared) # 输出结果:{1, 2}