python 修饰器
起因
最近在考研,休息的时候搞了几个QQ机器人。用一些优秀的开源库时发现了一些方便的特征类似于
# 收到"报时"时回复当前时间
@pcqq.on_full("报时")
async def NowTime(session: pcqq.Session):
await session.send_msg({"type": "at", "qq": session.user_id}, pcqq.utils.time_lapse(0))
,小小学习一下python修饰器后,我研究了一下这种方式的原理。
python修饰器的作用
python修饰器最简单直白的解释就是给修饰的函数套上一层函数,你套上的这层函数会在代码解析的时候就会被调用,而你套上函数的返回值会被用来代替被修饰函数对应的标识符。
1.修饰器函数什么时候被调用
def callme_when_at(func):
print("decorator function is called!")
return func
@callme_when_at
def fun():
pass
# decorator function is called!
上面代码我们没有显式调用callme_when_at,但是呢callme_when_at函数在当作修饰器时是被调用了的。
2.修饰器返回值被拿来干什么了?
def callme_when_at(func):
print("decorator function is called!")
return func
def callme_when_at1(func):
print("decorator function is called!")
# 我们直接写一个新函数,压根就不调用原函数,看看发生了什么
def fake():
print("fake fun")
return fake
@callme_when_at
def fun():
print("ddd")
@callme_when_at1
def fun1():
print("real fun")
fun()
fun1()
# decorator function is called!
# decorator function is called!
# ddd
# fake fun
事实表明,修饰器函数返回的是一个函数对象,并且这个函数对象会替代原来的函数调用。比如被at1修饰的fun1,在被调用的实际上执行的是修饰器返回的函数里的代码。
3.利用这个特征干点事?
def log(func):
def wrap():
print("function:{} called".format(func.__name__))
func()
return wrap
@log
def fun():
print("hello")
fun()
# function:fun called
# hello
比如在函数被运行时打印一下日志
4.加强版,我们要修饰器要参数怎么办?
class dec():
def __init__(self,msg) -> None:
print(msg)
def __call__(self, func):
def wrap(*args,**kwargs):
print("do something before")
func()
print("do something after")
return wrap
@dec("参数")
def f():
print("hello from f")
f()
# 参数
# do something before
# hello from f
# do something after
可以用一个类来实现,在初始化时写入参数,并且实现类的调用方法返回新的函数
5.怎么实现第三方库的效果?
tasklist = []
class task():
def __init__(self,task_id) -> None:
self.id = task_id
self.handler = ...
def __call__(self, fun):
tasklist.append(self)
self.handler = fun
def f():
print("wrapped")
return f
@task(1)
def fun1():
print("f1")
@task(2)
def fun2():
print("f2")
def main():
for t in tasklist:
print("task-{}".format(t.id))
t.handler()
if __name__=='__main__':
main()
# task-1
# f1
# task-2
# f2
定义一个修饰器类,在初始化的时候写入一些修饰器参数,在被修饰器被调用的时候把修饰的函数存入handler,把对应对象插入到任务列表,在主函数中遍历任务列表并调用每个task对象的handler。