f众所周知,Python有一个很像Java注解的东西,叫做装饰器
又众所周知,装饰器本质是一个高阶函数, 使用@符号调用装饰器相当于将该函数传入装饰器的参数再赋值给原函数
也就是说这段代码
@decorator
def fun(*args):
...
相当于这段代码
def fun(*args):
...
fun = decorator(fun)
好,基础的装饰器原理讲完了,接下来我们来尝试一下一些奇怪的装饰器——
带参数的装饰器
我们有时候见过一些带参数的装饰器, 比如像这种
@App(title="Hello World!")
def MyApp():
...
其实这种“装饰器”就是一个返回装饰器的函数,也就是这段代码中 " App(title="Hello world!") "这个是一个部分,它返回一个装饰器,装饰了MyApp这个函数
这个App函数签名大概就长这样:
def App(*, title: str, ...) -> Callable[[Callable[[...], None]], Callable[[...], None]]
装饰类的装饰器
我们也见过修饰类的装饰器, 比如dataclasses模块中的数据类装饰器@dataclass:
@dataclass
class PointType:
x: float
y: float
这种装饰器是如何实现的呢?
其实也是跟普通装饰器差不多,只是将传入装饰器的参数和装饰器的返回值修改为Type类型,就比如这样的装饰器:
def ClassDecorator(clss: Type[object]) -> Type[object]:
class _inner_class(clss):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
print("Decorated Class.")
return _inner_class
……然后这样修饰……
@ClassDecorator
class MyClass:
def __init__(self, *args, **kwargs)-> None:
...
这样当我们创建MyClass对象, 相当于创建了一个_inner_class类的对象,执行完MyClass的__init__方法的代码后,然后打印"Decorated Class."字符串
其他离谱的装饰器玩法
使用lambda做装饰器
没错, python支持使用lambda表达式做装饰器!
这意味着你的代码甚至可以这样子写……
@(lambda fun: (lambda : fun("Hello world")))
def hello(s: str) -> None:
print(s)
# 相对之下正经的装饰器写法
def __helloworld_decorator(fun: Callable[[str], None]) -> Callable[[], None]]:
def __closure_fun() -> None:
fun("Hello world")
return __closure_fun
@__helloworld_decorator
def hello(s: str) -> None:
print(s)
# 相等于
def hello() -> None:
print("Hello world")
当然, 不建议直接使用lambda作为装饰器,这样做又麻烦又降低可读性。
使用Functor(可调用对象)做装饰器
python也允许使用带__call__方法的类实例对象作为函数的装饰器
比如这样的:
class DecoratorClass:
def __init__(self):
...
def hello(self)-> None:
print("hello")
def __call__(self, fun) -> fun:
def hel():
print("pre decorated")
fun()
return hel
#可以这样使用
dec = DecoratorClass()
@dec
def pr() -> None:
print(dec.hello())
使用装饰器的写法使用某些函数
借助装饰器的性质,我们可以将某些高阶函数当做装饰器来用
比如Pyside6中的信号绑定槽函数我们就可以使用装饰器优雅地来写——
@Btn.clicked.connect
@Slot()
def _() -> None:
# When the Btn click
...