装饰器学习

官网文档

https://docs.python.org/zh-cn/3/reference/compound_stmts.html#function-definitions
https://docs.python.org/zh-cn/3/reference/compound_stmts.html#class-definitions

python工匠

https://pengzhangzhi.github.io/one-python-craftsman/zh_CN/8-tips-on-decorators.html

多装饰器顺序

  1. 导入时输出111 444 555 222,运行时输出333 666。

    • test == test1()(test2()(test)) 为 True
    • 导入时
      • test1()(test2()(test)),首先执行test1(),再执行test2(),得到dec1(dec2(test))。
      • 然后执行dec1(dec2(test)),首先执行dec2(test),再执行dec1(),得到wrapper1,此时wrapper1中func1即为wrapper2
    • 运行时
      • 执行wrapper1,返回func1()即wrapper2()
      • wrapper2()运行,返回真正的test()
    def test1():
        print(111)
    
        def dec1(func):
            print(222)
    
            def wrapper1():
                print(333)
                return func1()
            return wrapper1
        return dec1
    
    
    def test2():
        print(444)
    
        def dec2(func):
            print(555)
    
            def wrapper2():
                print(666)
                return func2()
            return wrapper2
        return dec2
    
    
    @test1()
    @test2()
    def test():
        print(777)
    
    
    # test == test1()(test2()(test)) # True
    
    test()
    
  2. 导入时顺序输出222 111,运行时输出333,注意**test == dec1(dec2(test))**为True

    def dec1(func):
        print(111)
        return func
    
    
    def dec2(func):
        print(222)
        return func
    
    
    @dec1
    @dec2
    def test():
        print(333)
    
    
    # test == dec1(dec2(test))
    
    # test()
    
  3. 导入时顺序输出111 333 444 222。注意test == dec1()(dec2()(test))为True

    def dec1():
        print(111)
    
        def wrapper(func):
            print(222)
            return func
        return wrapper
    
    
    def dec2():
        print(333)
    
        def wrapper(func):
            print(444)
            return func
        return wrapper
    
    
    @dec1()
    @dec2()
    def test():
        print(555)
    
    
    # test == dec1()(dec2()(test))
    
  4. 变式

    def test(func):
        print(222)
    
        def dec():
            func()
        return dec
    
    
    @test
    def a():
        print(111)
    

实用例子

import time


class Timeit:

    def __init__(self, func=None, arg0=None):
        self._func = func
        self.arg0 = arg0
        self.calls = 0

    def __call__(self, *args, **kwargs):
        print(f'参数1:{self.arg0}')

        if self._func:
            self.calls += 1
            start_time = time.time()
            r0 = self._func(*args, **kwargs)
            print(f'耗时:{time.time() - start_time}')
            print("调用 %s() %d 次" % (self._func.__name__, self.calls))
            return r0
        else:
            func = args[0]

            def wrapper(*ars, **kws):
                self.calls += 1
                start_time1 = time.time()
                r1 = func(*ars, **kws)
                print(f'耗时:{time.time() - start_time1}')
                print("调用 %s() %d 次" % (func.__name__, self.calls))
                return r1

            return wrapper


@Timeit
def foo1(sleep_time):
    print(f'睡眠{sleep_time}秒')
    time.sleep(sleep_time)

# fool1 = Timeit(fool1)


@Timeit(arg0=99999)
def foo(sleep_time):
    print(f'睡眠{sleep_time}秒')
    time.sleep(sleep_time)

# foo = Timeit(arg0=99999)(foo)

if __name__ == '__main__':
    foo1(3)
    foo1(2)
    for _ in range(3):
        foo(1)

装饰器实现对象\实例的替换

from functools import update_wrapper


class DelayedStart:
    def __init__(self, func):
        update_wrapper(self, func)  # 和 @functools.wraps(func) 一致
        self.func = func

    def __call__(self, *args, **kwargs):
        print("Wait for 1 sec before starting...")
        time.sleep(1)
        self.func(*args, **kwargs)

    def eager_call(self, *args, **kwargs):
        print("Call without delay")
        self.func(*args, **kwargs)


@DelayedStart
def hello():
    print("Hello, world.")


####################
# 输出
####################
>>hello
<__main__.DelayedStart object at 0x04793310>

>>type(hello)
<class '__main__.DelayedStart'>

>>hello.__name__  被包装函数的元数据更新到包装者上
'hello'

>>hello()
Wait for 1 sec before starting...
Hello, world.

>>hello.eager_call()  由上述得hello对象替换为了DelayedStart,就可以调用eager_call方法
Call without delay
Hello, world.

装饰器注入参数

import random
from functools import wraps


def provide_num(min_num, max_num):
    """装饰器,随机生成[min_num, max_num]范围内的整数,并注入函数的第一个参数"""

    def inner(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            num = random.randint(min_num, max_num)
            if isinstance(args[0], int):
                args = (args[0] + num, )
            else:
                args = (args[0], args[1] + num)
            func(*args, **kwargs)

        return wrapper

    return inner


@provide_num(1, 10)
def print_num(num):
    print(num)


class Foo:
    @provide_num(1, 10)
    def print_num(self, num):
        print(num)


print_num(777)
Foo().print_num(777)

如果使用这样的装饰器则num的传参会覆盖类方法的第一个默认参数self

def provide_num_error(min_num, max_num):
    """装饰器,随机生成[min_num, max_num]范围内的整数,并注入函数的第一个参数"""

    def wrapper(func):
        @wraps(func)
        def dec(*args, **kwargs):
            num = random.randint(min_num, max_num)
            func(num, *args, **kwargs)
        return dec
    return wrapper

细品

def foo():
    print(222)

    def dec(func):
        return func
    return dec


@foo()
def a():
    print(111)


a()
def foo(func):
    print(222)

    def dec():
        return func()
    return dec


@foo
def a():
    print(111)


a()

"""
222
111
"""
def foo(func):
    print(222)

    def dec():
        return func
    return dec


@foo
def a():
    print(111)


a()

"""
222
"""
class Foo:
    def register(self, check, *tags):
        def inner(check):
            return check

        if callable(check):
            return inner(check)
        else:
            if check:
                tags += (check,)
            return inner


foo = Foo()


@foo.register("my_tag")
def demo(aaa):
    print(aaa)

"""
运行时 -> foo.register("my_tag")(demo)
       此时check为"my_tag",返回inner
       -> inner(demo)
"""
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值