Python之路(四)——Python 三器(迭代器、生成器、装饰器)
本节内容
- 迭代器
- 生成器
- 装饰器
一、迭代器(Iterator)
可迭代对象(Iterable Object)
- 表面来看,只要可以用 for...in...进行遍历元素的对象就是可迭代对象
- 语法层面,如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象
from collections import Iterable # 判断一个对象是可迭代对象 l = [1, 23] d = {'name': 'alex', 'age': 12} t = (1, 3, 4, 5, 7, 3) set1 = set(t) s = "1234343" l_iter = [l, d, t, set1, s] # 判断方法1 for obj in l_iter: print(isinstance(obj, Iterable)) # 判断方法2 for obj in l_iter: if '__iter__' in dir(obj): print(obj) class MyList(object): def __init__(self): self.items = [] def add(self, val): self.items.append(val) def __iter__(self): myiterator = MyIterator(self) return myiterator class MyIterator(object): def __init__(self, mylist): self.mylist = mylist self.current = 0 def __next__(self): if self.current < len(self.mylist.items): item = self.mylist.items[self.current] self.current += 1 return item else: raise StopIteration def __iter__(self): return self if __name__ == '__main__': mylist = MyList() mylist.add(1) mylist.add(2) mylist.add(3) mylist.add(4) mylist.add(5) print(isinstance(mylist,Iterable)) #True # 判断方法3 可以使用for ...in ..
可迭代对象 vs 迭代器
- 可迭代对象:实现了__iter__方法;迭代器:实现了__next__方法
- 可以被
next()
函数调用并不断返回下一个值的对象称为迭代器(Iterator),直到没有数据时抛出StopIteration
错误。不断next取下一个值的数据流对象。可以看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。 - 迭代器一定是可迭代对象,但可迭代对象不一定是迭代器。如:[1,3,4] 可以迭代,但不是迭代器
二、生成器(Generator)
定义
特殊的迭代器,仅保存计算规则不提前生成结果,需要(next方法调用)时进行计算生成
#生成器 #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],需求把列表里的每个值加1 a = [i for i in range(10)] b = [i+1 for i in a] print(b) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] #求0,100000w个数,每个数的平方是多少? # a = [i**2 for i in range(1000000000)] #内存要爆炸 #使用生成器 g = (i**2 for i in range(1000000000)) #[] 变 () for i in range(10): #取前100 的计算 print(next(g))
两种生成器形式
- 生成器表达式:上述
- 生成器函数
# #使用生成器 # g = (i**2 for i in range(1000000000)) #[] 变 () # for i in range(10): # #取前100 的计算 # print(next(g)) #生成器函数 #老方法: # def fib(max_num): # n, a, b = 0, 0, 1 # res = [] # while n < max_num: # res.append(b) # a, b = b, a + b # n = n + 1 # return res # # #求10、15位斐波那契数列 # a =fib(10) # b = fib(15) # print(a) # print(b) #问题: 求10000w 之前的斐波那契数列 #方法1: 修改代码,取1w 保存存列表,之后保存文件,循环处理 # 方法2:生成器,即:生成斐波那契数列与保存数据的逻辑 解耦 def fib(max_num): n, a, b = 0, 0, 1 while n < max_num: yield b a, b = b, a + b n = n + 1 return 'done' g_fib = fib(100000000) l = [] # 1000行进行操作 # for i in range(1000): # #取值 # next(g_fib) # #操作 # 需求二、取100..110位的数 for i in range(110): print(next(g_fib)) if i >= 100-1: l.append(next(g_fib)) else: #不做处理 pass print(l)
三、装饰器(Wrapper)
什么是装饰器以及有什么用
- 场景一
# def say_hello(): # print("hello!") #需求一:给打印格式调整: [DEBUG]: Enter say_hello() # def say_hello(): # print("[DEBUG]: Enter say_hello()") # print("hello!")
- 场景二
#需求二:这里有200个这样的函数,怎么做? #方法一: 不睡觉,一个个添加 #方法二: 找到需要修改的文件;匹配对应行,修改之后加入写入文件 # def debug(): # import inspect # caller_name = inspect.stack()[1][3] # print("[DEBUG]: enter {}()".format(caller_name)) # # def say_hello(): # debug() # print("hello!") # 遍历文件 # def all_files(): # #找到所有此类函数的文件
- 场景三
# 需求三、不能修改源代码,风险评估太大 #方法: # def wrapper_fun(f): # print("[DEBUG]: Enter %s()" %f.__name__) # f() # 方法: 修改所有调用函数的地方:f() -> wrapper(f) # if __name__ == '__main__': # #在每个调用处修改 # wrapper_fun(say_hello)
# 问题:mmp,好多地方前都是直接写函数名调用的。
- 场景四
def debug(f): def wrapper(): #需要包裹的内容 print("[DEBUG]: Enter %s()" %f.__name__) return f() return wrapper def say_hello(): print("hello!") say_hello = debug(say_hello) if __name__ == '__main__': #不用在调用处修改 say_hello()
被装饰函数含有参数
#原函数 def say_hello(name,age=18): print("hello! %s ,your age:%s。" %(name,age)) #装饰器 def log(f): def wrapper(*args, **kwargs): print("调用前") f(*args, **kwargs) print("调用后") return wrapper say_hello = log(say_hello) #自动完成参数传递 say_hello("alex") # 调用前 # hello! alex ,your age:18。 # 调用后
装饰函数需要参数《终》
#简化代码 def log(error_level="Info"): def inner(f): def wrapper(*args, **kwargs): if error_level == "Error": print("记录日志") f(*args, **kwargs) return wrapper return inner #原函数 @log("Error") # 相当于: inner = log("Error");say_hello = inner(say_hello) def say_hello(name,age=18): print("hello! %s ,your age:%s。" %(name,age)) if __name__ == "__main__": say_hello("alex")
装饰特性总结:
- 是高阶函数(有函数作为参数、返回值)
- 有函数嵌套