装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象 它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的代码并继续重用
上面的函数a_new_decorator就是一个装饰器 现在python允许装饰器调用使用@语法糖
下面使用@语法糖重构上述装饰器:
import functools def a_new_decorator(a_func): def wrapTheFunction(): print("在函数调用前我正在做一些无聊的事情") a_func() print("终于结束函数调用了,我都快累死了") return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): print("我是一个需要装饰的函数") print(a_function_requiring_decoration.__name__) a_function_requiring_decoration()上述语法糖"@a_new_decorator"等价于 未重构之前的“a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)“
我们发现“a_function_requiring_decoration._name_”返回的是wrapTheFunction,也就是说原函数在经历装饰器后函数的名字和函数的注释、文档都被修改成了装饰器闭包内的函数
我们导入一个模块
import functools/from functools import wraps
import functools def a_new_decorator(a_func): @functools.wraps(a_func) def wrapTheFunction(): print("在函数调用前我正在做一些无聊的事情") a_func() print("终于结束函数调用了,我都快累死了") return wrapTheFunction @a_new_decorator def a_function_requiring_decoration(): print("我是一个需要装饰的函数") print(a_function_requiring_decoration.__name__) a_function_requiring_decoration()就可以解决这个问题了
也可以通过以下两个方法:
使用decorator.py文件 decorator.py 是一个非常简单的装饰器加强包。你可以很直观的先定义包装函数wrapper(),再使用decorate(func, wrapper)方法就可以完成一个装饰器。 decorator.py实现的装饰器能完整保留原函数的name,doc和args 唯一有问题的就是inspect.getsource(func)返回的还是装饰器的源代码需要改成inspect.getsource(func._wrapped_)import decorator
import decorator import datetime # 装饰器的优化 def wrapper(func, *args, **kwargs): print("我正在装扮自己") return func(*args, **kwargs) def logging(func): # 用wrapper装饰func return decorator.decorate(func, wrapper) # @logging def hi(): print("my name is Q") a= logging(hi) a() 使用 wrapt文件import wrapt
wrapt是一个功能非常完善的包,用于实现各种装饰器 使用wrapt实现的装饰器不需要担心之前inspect中遇到的所有问题,因为它都帮你处理了,甚至inspect.getsource(func)也准确无误
import wrapt @wrapt.decorator def logging(wrapped, instance, *args, **kwargs): print("我是个装饰器的外套") return wrapped(*args, **kwargs) @logging def sayHi(*args, **kwargs): print("Hi") sayHi()在装饰器外再套一层装饰器,专门用于向装饰器内部传递参数
import functools def add_2_somenumber(num, add_num=1): print("---1---") def know_whatToDo(func): print("---2---") @functools.wraps(func) def wirte_function_name(*arg, **kwargs): print("---3---") print(func.__name__, " was called at here") print("as a result, the answer is %d" % (num + add_num)) return func(*arg, **kwargs) return wirte_function_name return know_whatToDo @add_2_somenumber(2,3) def print_result(): print("开始活动了") print("---4---") print_result()装饰器函数其实是这样一个接口约束 它必须接受一个callable对象作为参数,然后返回一个callable对象 在Python中一般callable对象都是函数 但也有例外,只要某个对象重载了__call__()方法,那么这个对象就是callable的
_init_:用来传递参数 _call_:用来构建装饰器
# 装饰类 import functools class logit(object): def __int__(self, logfile="out.log"): self.login_file = logfile def __call__(self, func): @functools.wraps(func) def wrapped_function(*arg, **kwarg): login_string = func.__name__+" was called " print(login_string) return func(*arg, **kwarg) return wrapped_function @logit(logfile="out.log") def myfunc_test(): print("这是类装饰器的产生") myfunc_test() 1、装饰器原则: 1)不能修改原函数 2)不能修改调用方式 2、装饰器通过嵌套函数和闭包实现 3、装饰器执行顺序:洋葱法则 4、装饰器通过语法糖“@”修饰 5、谨记装饰器返回的是持有被装饰函数引用的闭包函数的引用这条原则属性有三个装饰器:setter, getter, deleter ,都是在property()的基础上做了一些封装 因为setter和deleter是property()的第二和第三个参数,不能直接套用@语法
class Classmate: def __init__(self): self.age = 15 @property def set_name(self, value): self.name = value student = Classmate() student.name="Q" print(student.age) print(student.name)有了@property装饰器的了解,这两个装饰器的原理是差不多的 @staticmethod返回的是一个staticmethod类对象 @classmethod返回的是一个classmethod类对象 他们都是调用的是各自的__init__()构造函数
发现普通的类不是可迭代对象 只用当当类对象中出现__iter__方法时,该类就变成了可迭代对象
class ClassMate(object): def __init__(self): self.names = [] def add_name(self, name): self.names.append(name) def __iter__(self): pass student = ClassMate() student.add_name("老王") student.add_name("小张") student.add_name("小李") # 当类对象中出现__iter__方法时,该类就变成了可迭代对象 print(isinstance(student, Iterable)) # 我们使用for循环遍历,发现还是不能够迭代类对象 # 这时候我们就可以使用类中的一个方法__next__,使其迭代对象 try: for i in student: print(i) except Exception as e: print("不能够进行迭代")生成器是一种特殊的迭代器 普通的生成器 只需要把迭代器生成时的中括号改成小括号
def get_test1(num): print(num) i = 0 print(i, end=" ") while i < num: print(i, end=" ") ret = yield i i += 1 n = get_test1(10) for i in range(10): next(n) def get_test2(num): print(num) i = 0 print(i, end=" ") while i < num: print(i, end=" ") ret = yield i print(ret) i += 1 n1 = get_test2(10) next(n1) n1.send("hahahaha") next(n1)当函数调用时出现了yield,它就变成了一个生成器 生成器可以通过_next_来获取数据
obj.send(msg)当程序进行时,使yield调用拥有返回值,其值为send传输过去的msg
注意:send方法调用时必须先调用next方法开始迭代生成
当子函数运行到yield时,程序会先将数值返回个函数调用,然后停止等待,一直到下一次获取生成器中数据时才启动
