能接受一个函数的函数或者类,可以当做一个装饰器,这个函数需要返回一个函数(并不是函数的调用),函数的参数应于被修饰的函数的参数相同
条件
能作为装饰器的函数应该具有以下特征
- 函数需要接受一个函数作为参数
- 函数需要返回一个函数,返回的函数的参数应于被修饰的函数的参数相同
- 注意是返回函数,而不是返回函数的调用
@ 就相当于执行返回的函数,也就是说@ 后面的函数先执行一次,拿到返回的函数,饭后执行返回的函数
- 在执行函数的时候,其实是在执行装饰器返回的函数,装饰器下面的函数,只是装饰器的参数
装饰器的简单使用
代码
def decorator(func):
def make_decorator():
print('现在开始装饰')
func()
print('现在结束装饰')
return make_decorator
@decorator
def test():
print('i am test')
test()
效果
现在开始装饰
i am test
现在结束装饰
装饰器传参
代码
def decorator(*args,**kwargs):
def start_decoration(func):
def make_decorator():
print(args,kwargs)
print('现在开始装饰')
func()
print('现在结束装饰')
return make_decorator
return start_decoration
@decorator("hello world")
def test():
print('i am test')
test()
打印如下
('hello world',) {'name': 'hello'}
现在开始装饰
i am test
现在结束装饰
代码的解释
- 因为装饰器需要传参,就需要类似的这种形式传参
@decorator("hello world") - 他会先进行
decorator("hello world") 函数的调用,而@后面也需要一个函数(不是一个函数的调用),这个函数需要满足装饰器的条件
- 所以在
decorator("hello world") 中需要返回一个函数,所以返回了start_decoration - 这个时候
@ 拿到的就是start_decoration 这个函数 start_decoration 满足装饰器的条件 - 而作为装饰器的函数的内部,也可以拿到我们通过装饰器传的参数
多个装饰器
代码
def decorator1(func):
def make_decorater(*args,**kwargs):
print('decorator1 start')
test_func = func(*args,**kwargs)
print('decorator1 end')
return test_func
return make_decorater
def decorator2(func):
def make_decorater(*args,**kwargs):
print('decorator2 start')
test_func = func(*args,**kwargs)
print('decorator2 end')
return test_func
return make_decorater
@decorator1
@decorator2
def test():
print('我是被装饰的函数')
test()
结果
decorator1 start
decorator2 start
我是被装饰的函数
decorator2 end
decorator1 end
知识点
先装饰,后执行
根据结果我们可以看出,被装饰的函数test大概被装饰城了下面的样子
def decorated_test():
ret = None
print('decorator1 start')
print('decorator2 start')
ret = test()
print('decorator2 end')
print('decorator1 end')
return ret
装饰的顺序
- 从下往上装饰,也就是说
decorator2 先进行装饰,decorator1 拿到decorator2 装饰后的结果之后,再进行装饰
执行的顺序
类装饰器
类装饰器比较容易理解,类装饰器是专门用于装饰类的,作为装饰器的可以是一个函数,也可以是一个类
条件
可以简单的记为,给谁装饰就要返回谁 (给函数装饰,最终返回函数,给类装饰,最终要返回类)
- 需要接受一个类为参数
- 需要返回当前的类
例子
代码
def decorator(cls):
cls.name = "decorator_name"
return cls
@decorator
class Animal:
pass
animal = Animal()
print(animal.name)
效果
会打印出decorator_name
类作为装饰器
- 当我们调用装饰器进行传参的时候,他会将类进行调用(实例化),实例化后还会进行一次调用,实例需要是一个可调用的对象,那么就必须加上
__call__ 方法 - 这个
__call__ 方法有一些要求
- 他接收函数作为参数,这个函数就是我们装饰的函数
- 返回一个函数,返回函数的参数应于被装饰的函数相同
代码
作为函数装饰器
from cgi import print_arguments
from typing import Any
class Decorator:
def __init__(self, name) -> None:
self.name = name
def __call__(self, func, *args: Any, **kwds: Any) -> Any:
print('调用了Decorator的__call__')
print(f"装饰器的名字{self.name}")
def inner(foo_self, *args):
print(foo_self.name)
print("我是装饰后的函数")
return func(foo_self, *args)
return inner
descirber = Decorator
class Foo:
def __init__(self):
self.name = 'Foo class'
@descirber('Decorator')
def foo(self):
return 'Foo 实例上的 foo'
f = Foo()
print(f.foo())
作为类装饰器
秉承原则:给谁装饰就要返回谁
class Class_decorator:
def __init__(self,cls) -> None:
self.cls = cls
def __call__(self, *args: Any, **kwds: Any) -> Any:
self.cls.name = "Class_decorator_name"
return self.cls
@Class_decorator
class Animal:
pass
animal = Animal()
print(animal.name)
注意事项
- 类作为类装饰器的时候,类作为参数的时候,是通过
__init__ 传递的 - 但实质上,当我们的装饰器需要传参的时候,想要拿到这个类,我们应该在
__call__ 这个方法中拿到。当不需要传参的时候,想要拿到这个类,就应该在__init__ 中拿到
|