python装饰器用法归总

什么是装饰器?

一切返回一个可调用对象的对象都可用作装饰器。
python 本着一切都是对象的宗旨。那么对象又有哪些类别呢?
答: 可调用对象(callable)和不可调用对象。
可调对象有:方法、函数、实现了__call__的对象以及python内部实现的一系列内构函数。

作用:非入侵式修改其它对象。

在不修改其它对象的代码块的前提下,对其功能进行影响。

都有哪些用法?

  1. 函数(方法)装饰器
  2. 类装饰器
  3. 不带参数装饰器
  4. 带参数装饰器

例子1 对function进行装饰

def descriptor(func):

    def wrapper(*args, **kwargs):
        print("参数列表:", args, kwargs)
        return func(*args, **kwargs)

    return wrapper

@descriptor
def simple1():
    print("simple1")


if __name__ == "__main__":
    print("simple1:", simple1)
    print("simple1 name:", simple1.__name__)
    simple1()

输出

simple1: <function descriptor.<locals>.wrapper at 0x000001FD154371C0>
simple1 name: wrapper
参数列表: () {}
simple1

于是乎,我们可以在实际的func调前后可以做一些特别的操作,比如日志跟踪,状态修改…
但是,请等一下:被装饰的对象已经不是原来的对象,比如名字变了。那么要想不变怎么做?

from functools import wraps
def descriptor(func):

	# 使用python 提供的wraps再装饰一下就好了
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("参数列表:", args, kwargs)
        return func(*args, **kwargs)

    return wrapper

@descriptor
def simple1():
    print("simple1")


if __name__ == "__main__":
    print("simple1:", simple1)
    print("simple1 name:", simple1.__name__)
    simple1()

输出:

simple1: <function simple1 at 0x0000011E488071C0>
simple1 name: simple1
参数列表: () {}
simple1

例子 2 function代参数装饰器

def descriptor1(*args, **kwargs):

    def function(wrapped):
        @wraps(wrapped)
        def wrapper(*wargs, **wkwargs):
            print("装饰的参数", args, kwargs)
            print("function参数", wargs, wkwargs)
            fn = wrapped(*wargs, **wkwargs)
            # to do some things
            return fn
        return wrapper
    return function

@descriptor1(1, 2, 3, a="ad", b="ac")
def test(*args, **kwargs):
    pass

if __name__ == "__main__":
    test("a", "b", j=23, k=54)

输出:

装饰的参数 (1, 2, 3) {'a': 'ad', 'b': 'ac'}
function参数 ('a', 'b') {'j': 23, 'k': 54}

宗旨没变:可调用对象可作为装饰器
@descriptor1(1, 2, 3, a="ad", b="ac") 在传参数后相当于调用一次函数(方法)返回一个可调用对象。

装饰器 装饰类

def descriptor2(*args, **kwargs):

    def function(wrapped):
        @wraps(wrapped)
        def wrapper(*wargs, **wkwargs):
            print("装饰的参数", args, kwargs)
            print("初始化参数", wargs, wkwargs)
            obj = wrapped(*wargs, **wkwargs)
            # to do some things
            setattr(obj, "test", "add a propter")
            return obj
        return wrapper
    return function


@descriptor2("A", d_type="class", name="A")
class A:
    pass

if __name__ == "__main__":
    a = A()
    print("a.test: ", a.test)

输出

装饰的参数 ('A',) {'d_type': 'class', 'name': 'A'}
初始化参数 () {}
a.test:  add a propter

装饰器 装饰 对象method

def descriptor1(*args, **kwargs):

    def function(wrapped):
        @wraps(wrapped)
        def wrapper(*wargs, **wkwargs):
            print("装饰的参数", args, kwargs)
            print("method参数", wargs, wkwargs)
            fn = wrapped(*wargs, **wkwargs)
            # to do some things
            return fn
        return wrapper
    return function
class B:
    @descriptor1("descriptor1参数")
    def test(self, *args, **kwarg):
        print("类B 的test参数 ", args, kwarg)



if __name__ == "__main__":
    b = B()
    b.test("b test arg")

输出:

装饰的参数 ('descriptor1参数',) {}
method参数 (<__main__.B object at 0x000002303E130CA0>, 'b test arg') {}
类B 的test参数  ('b test arg',) {}

b.test("b test arg") 本应该打印一个参数'b test arg'
但是 还打印了<__main__.B object at 0x000002303E130CA0>, 这是因self是作为对象的方法的第一参数传入的。同理带参数的方法的装饰器可以参考例2

类作为装饰器 装饰其它对象

class C:

    def __init__(self, *args, **kwargs):
        print("类C init 参数", args, kwargs)

    def __call__(self, *args, **kwargs):
        print("B call参数", args, kwargs)

@C
class D:

    def __init__(self):
        print("对象D")

输出:

类C init 参数 (<class '__main__.D'>,) {}
B call参数 () {}

本质上和function作装饰一样,只不过类生成的对象得可调用。
但值得注意的是,类装饰器可以带参数去装饰其它类对象。

class C:

    def __init__(self, *args, **kwargs):
        print("类C init 参数", args, kwargs)

    def __call__(self, *args, **kwargs):
        print("C call参数", args, kwargs)
        return self

@C("C作为装饰器带参数了")
class D:

    def __init__(self):
        print("对象D")

if __name__ == "__main__":
    d = D()

输出:

类C init 参数 ('C作为装饰器带参数了',) {}
C call参数 (<class '__main__.D'>,) {}
C call参数 () {}

但是这里不对!!!! C类的call调用了两次,且D类的初始化没用执行(类没有生成对象),原因?类型在传参数后返了一对对象,对象要生成装饰器,第一次第用了__call__, 在作为可调用代码在D中执行又调了一次,这次没有传参数。所以可以这样写。

class C:

    def __init__(self, *args, **kwargs):
        print("类C init 参数", args, kwargs)

    def __call__(self, wrapped):
        print("C call参数", wrapped)
        def fn(*args, **kwargs):
            
            print("参数 fn", args, kwargs)
            
            return wrapped(*args, **kwargs)
        return fn

@C("C作为装饰器带参数了")
class D:

    def __init__(self):
        print("对象D")

if __name__ == "__main__":
    d = D()

输出结果为:

类C init 参数 ('C作为装饰器带参数了',) {}
C call参数 <class '__main__.D'>
参数 fn () {}
对象D

这下结果就符合预期的了。
最后,类作为装饰也可以装饰其它的函数,是一样的。
最后再强调一下,一切返回结果为可调用对象的对象都可以作为装饰器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是魏毛毛啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值