# =========================第九章:元编程=============================
# ----------------在函数上添加包装器-----------------------
# 在函数上添加一个包装器,增加额外的操作处理(比如日志、计时等)
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