python可以从可迭代对象中获取迭代器
# 第一种方式判断对象是不是可迭代对象 print('__iter__' in dir(list)) print('__iter__' in dir(tuple)) print('__iter__' in dir(dict)) print('__iter__' in dir(set)) print('__iter__' in dir(str)) print('__iter__' in dir(int)) print('__iter__' in dir(bool)) print('__iter__' in dir([1,2,3])) # 输出 True True True True True False False True # 第二种方式判断对象是不是可迭代对象 from collections import Iterable print(isinstance('abc', Iterable)) print(isinstance({1, 2, 3}, Iterable)) print(isinstance(1, Iterable)) 输出如下: True True False
通过内置函数iter(iteratable) 返回可迭代对象的迭代器
next(iterator) 返回可迭代对象的下一个元素或者抛出StopIteration异常
it = iter([1,2,3,4]) next(it) > 1 next(it) > 2 next(it) > 3 next(it) > 4 next(it) > StopIteration
class MyIter: def __init__(self): self.storage = [1,2,3] def __iter__(self): return self def __next__(self): try: return self.storage.pop() except Exception as e: return e mi = MyIter() # mi是一个可迭代对象,因为实现了__iter__方法 it = iter(mi) # it是一个迭代器 next(it) # 执行MyIter的__next__方法
生成器也是迭代器,但更加优雅。使用生成器,我们可以实现与迭代器相同的功能,但不必在类中编写iter()和next()函数
如果迭代器是人类,生成器就是人类中的一种,比如黄种人
# 方式1 yield def gen(): yield 1 yield 2 yield 3 g = gen() # g是一个genenrator对象 next(g) > 1 next(g) > 2 next(g) > 3 next(g) > StopInteration # 方式2 推导式 li = [i*i for i in range(10000)] # 这是一句列表推导式,使用列表推导式会把0~9999的平方分别进行平方存储到这个列表中 # 生成器推导式 gen = (i*i for i in range(10000)) gen > <generator object <genexpr> at 0x000001FAE47B8CF0> next(gen) > 0 next(gen) > 1 next(gen) > 4 # 生成器与列表推导式相比可以节省内存空间,在你需要的时候获取值,而不是一次加载到内存中
装饰器首先要学的是闭包
def out_func(data): def inner_func(): msg = "hello" print(f"{msg}-{data}") return inner_func
闭包的两个条件:
# >符号开头的代表在命令行或者jupyter下执行命令 # 计算移动平均值的类 class Average(): def __init__(self): self.series = [] def __call__(self, new_value): self.series.append(new_value) total = sum(self.series) return total/len(self.series) > avg = Average() > avg(10) > 10 > avg(11) > 10.5 # 使用函数的形式 def make_average(): series = [] def average(new_value): series.append(new_value) total = sum(series) return total/len(series) return average > avg = make_average() # avg=average函数 > avg(10) # avg(10) series=[10] new_value=10 > 10 > avg(11) # avg(11) series=[10,11] new_value=11 > 10.5
例子中的series列表就是一个自由变量,指未在本地作用域中绑定的变量,闭包延申到函数作用域之外,包含自由变量series的绑定
如图所示,闭包函数通过__code__的co_varnames返回闭包函数作用域内的所有变量,co_freevars返回不在闭包函数作用域内,在外层函数作用域内的变量,也就是“自由变量”
闭包函数的__closure__返回一个cell对象,cell对象是一个列表,获取列表的cell_contents属性可以拿到自由变量的值
def make_average(): total=0 count=0 def average(new_value): total += new_value count += 1 return total/count return average avg = make_average() avg(10) > UnboundLocalError: local variable 'total' referenced before assignment # 先说一下解决方案,python3有一个关键字 nonlocal def make_average(): total=0 count=0 def average(new_value): nonlocal total,count total += new_value count += 1 return total/count return average avg = make_average() avg(10) > 10.0
当内部函数执行total+=new_value,相当于total被重新赋值,将自由变量变为局部变量,total就不是自由变量,就不会被保存到闭包中,所以报错
当使用series列表的时候,list.append(value),并没有改变列表的地址,利用了列表是可变对象的这个事实
nonlocal的作用就是声明这个不是局部变量,而是一个自由变量,这样才会被解释器重新保存到闭包中,对于python2中没有nonlocal这个关键字,只能利用可变对象做为闭包的自由变量。
先了解闭包的原理,是学习装饰器的必要条件,话不多说,还是直接上代码,实践才是检验真理的唯一标准,哈哈哈
def wrap(obj): return obj @wrap def say_hello(): return "hello world" say_hello() # 等同于warp(sayhello)() > "hello world" wrap(say_hello) #返回say_hello对象 > <function __main__.say_hello()> wrap(say_hello)() #执行say_hello > "hello world"
# 嵌套函数的装饰器 def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper def greet(): print('hello world') greet = my_decorator(greet) # greet = wrapper(wrapper是调用my_decoraor返回的函数对象),并且把greet放进闭包 greet() # 执行wrapper() 先输出print('wrapper of decorator'),在调用greet(),输出print('hello world') # 输出 >wrapper of decorator >hello world # @语法糖 def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper @my_decorator #@是python的语法糖等同于my_decorator(greet) def greet(): print('hello world') greet() # 输出 >wrapper of decorator >hello world
# 多层闭包 # repeat重复输出,num指重复输出次数 def repeat(num): def my_decorator(func): @functools.wraps(func)# 如果有这句 被装饰函数的__name__是被装饰函数本身的名字,如果没有,__name__不论被装饰函数是谁,都返回wrapper def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator') func(*args, **kwargs) return wrapper return my_decorator @repeat(4) def greet(message): print(message) greet('hello world') # 输出: > wrapper of decorator > hello world > wrapper of decorator > hello world > wrapper of decorator > hello world > wrapper of decorator > hello world greet.__name__ #输出wrapper # functools.wrap会保留原函数的元信息
类装饰器主要依赖函数__call__ ,因此我们主要重写__call__即可。
每当调用一个类的实例,函数__call__就会执行一次。
class Count: def __init__(self, func): self.func = func self.num_calls = 0 def __call__(self, *args, **kwargs): self.num_calls += 1 print('num of calls is: {}'.format(self.num_calls)) return self.func(*args, **kwargs) @Count def example(): print("hello world") example() # 输出 num of calls is: 1 hello world example() # 输出 num of calls is: 2 hello world
import functools def my_decorator1(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator1') func(*args, **kwargs) return wrapper def my_decorator2(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('execute decorator2') func(*args, **kwargs) return wrapper @my_decorator1 @my_decorator2 def greet(message): print(message) greet('hello world') #相当于my_decorator1(my_decorator2(greet('hello world'))) # 输出 execute decorator1 execute decorator2 hello world