python修饰器以及常用修饰器分析


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。


文章作者: f19
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 f19 !
  目录