装饰器
装饰器本质上就是一个函数,这个函数接收其他函数作为参考,并将其以一个新的修改后的函数替代它。
def wrap(f):
return f
@wrap
def test():
pass
上面的过程类似于:
f = wrap(test)
简单装饰器
def check_func(func):
def wrapper(*args, **kwargs):
if kwargs.get("username") != "admin":
raise Exception("This user is not allowed!")
return func(*args, **kwargs)
return wrapper
@check_func
def get(username="someone"):
pass
但是装饰器存在问题:新函数缺少很多原函数的属性,比如文档字符串和名字。
>>> print(get.__name__)
'wrapper'
Python 中内置的 functools
模块通过其 update_wrapper
函数解决了这个问题,它会复制这些属性给这个装饰器本身。
>>> get = functools.update_wrapper(check_func, get)
>>> print(get.__name__)
'get'
手动调用 update_wrapper
创建装饰器很不方便,所以提供了 wraps
函数。
from functools import wraps
def check_func(func):
@wraps(func)
def wrapper(*args, **kwargs):
if kwargs.get("username") != "admin":
raise Exception("This user is not allowed!")
return func(*args, **kwargs)
return wrapper
@check_func
def get(username="someone"):
pass
>>> print(get.__name__)
'get'
目前获取被装饰函数的方法比较麻烦,可以采用 inspect
提取函数的签名。
from functools import wraps
import inspect
def check_func(func):
@wraps(func)
def wrapper(*args, **kwargs):
func_args = inspect.getcallargs(func, *args, **kwargs)
if func_args.get("username") != "admin":
raise Exception("This user is not allowed!")
return func(*args, **kwargs)
return wrapper
@check_func
def get(username="someone"):
pass
带参数的装饰器
from functools import wraps
import inspect
def check_func(level):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
func_args = inspect.getcallargs(func, *args, **kwargs)
if level == "info":
if func_args.get("username") != "admin":
raise Exception("This user is not allowed!")
else:
print(level)
return func(*args, **kwargs)
return wrapper
return decorator
@check_func(level="warn")
def get(username="someone"):
pass
>>> get(username="someone")
'warn'
类装饰器
装饰器函数其实是这样一个接口约束,它必须接受一个 callable
对象作为参数,然后返回一个 callable
对象。在 Python 中一般 callable
对象都是函数,但也有例外。只要某个对象重载了 __call__()
方法,那么这个对象就是 callable
的。
class Logging(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(self.func.__name__)
return self.func(*args, **kwargs)
@Logging
def say(something):
print("say {}!".format(something))
带参数的类装饰器
from functools import wraps
class Logging(object):
def __init__(self, level='INFO'):
self.level = level
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
print("[{level}]: enter function {func}()".format(level=self.level, func=func.__name__))
return func(*args, **kwargs)
return wrapper
@Logging(level='INFO')
def say(something):
print("say {}!".format(something))
装饰器调用顺序:
@a
@b
@c
def f ():
pass
f = a(b(c(f)))
上下文管理器
上下文对象的简单实现:关键点为 __enter__
和 __exit__
class MyContext(object):
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, traceback):
pass
上下文管理协议的使用场景:
- 调用方法 A
- 执行一段代码
- 调用方法 B
基于 contextlib.contextmanager
实现
import contextlib
@contextlib.contextmanager
def MyContext():
yield
可以同时使用多个上下文管理器
with open("file1", "r") as source, open("file2", "w") as desination:
desination.write(source.read())
单分发器
import functools
class SnareDrum(object): pass
class Cymbal(object): pass
class Stick(object): pass
class Brushes(object): pass
@functools.singledispatch
def play(instrument, accessory):
raise NotImplementedError("Cannot play these")
@play.register(SnareDrum)
def _(instrument, accessory):
if isinstance(accessory, Stick):
return "POC!"
if isinstance(accessory, Brushes):
return "SHHHH!"
raise NotImplementedError("Cannot play these")
print(play(SnareDrum(), Stick()))