# =========================第九章:元编程============================= # ----------------在函数上添加包装器----------------------- # 在函数上添加一个包装器,增加额外的操作处理(比如日志、计时等) import time from functools import wraps def timethis(func): @wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end - start) return result return wrapper @timethis def countdown(n): while n > 0: n -= 1 countdown(10000000) # 一个装饰器就是一个函数,它接受一个函数作为参数并返回一个新的函数 # 任何时候定义装饰器的时候,都应该使用functools 库中的@wraps 装饰器来注解底层包装函数 # 在编写装饰器的时候复制元信息是一个非常重要的部分。如果你忘记了使用@wraps,那么你会发现被装饰函数丢失了所有有用的信息 from functools import wraps import logging def logged(level, name=None, meaasge=None): def decorate(func): logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = meaasge if meaasge else func.__name__ @wraps(func) def wrapper(*args, **kwargs): log.log(level, logmsg) return func(*args, **kwargs) return wrapper return decorate @logged(logging.DEBUG) def add(x, y): return x + y """ 最外层的函数logged()接受参数并将它们作用在内部的装饰器函数上面。 内层的函数decorate() 接受一个函数作为参数, 然后在函数上面放置一个包装器。 这里的关键点是包装器是可以使用传递给logged() 的参数的 """ from inspect import signature from functools import wraps def typeassert(*ty_args, **ty_kwargs): def decorate(func): if not __debug__: return func sig = signature(func) bound_types = sig.bind_partial(*ty_args, **ty_kwargs).arguments @wraps(func) def wrapper(*args, **kwargs): bound_values = sig.bind(*args, **kwargs) for name, value in bound_values.arguments.items(): if name in bound_types: if not isinstance(value, bound_types[name]): raise TypeError('Argument {} must be {}'.format(name, bound_types[name])) return func(*args, **kwargs) return wrapper return decorate """ 可以看出这个装饰器非常灵活,既可以指定所有参数类型,也可以只指定部分。 并且可以通过位置或关键字来指定参数类型 """ @typeassert(int, z=int) def spam(x, y, z=24): print(x, y, z) spam(1, 2, 3) spam(1, '2', 3) # spam(1, 'a', 'b') # ---------------------------------------------------------------------------------------------- # ------------------将装饰器定义为类的一部分-------------------------------- from functools import wraps class A: # Decorator as an instance method def decorator1(self, func): @wraps(func) def wrapper(*args, **kwargs): print('Decorator 1') return func(*args, **kwargs) return wrapper # Decorator as a class method @classmethod def decorator2(cls, func): @wraps(func) def wrapper(*args, **kwargs): print('Decorator 2') return func(*args, **kwargs) return wrapper # 一个是实例调用,一个是类调用 a = A() @a.decorator1 def spam(): pass @A.decorator2 def grok(): pass # ----------------------------------------------------------------------- # 将装饰器定义为类 # 为了将装饰器定义成一个实例,你需要确保它实现了__call__() 和__get__() 方法 import types from functools import wraps class Profiled: def __init__(self, func): wraps(func)(self) self.ncalls = 0 def __call__(self, *args, **kwargs): self.ncalls += 1 return self.__wrapped__(*args, **kwargs) def __get__(self, instance, cls): if instance is None: return self else: return types.MethodType(self, instance) @Profiled def add(x, y): return x + y class Spam: @Profiled def bar(self, x): print(self, x) # -------------------------------------------------------------------------------- # ?????????????????????????????332