Python自学成才之路 带有参数的装饰器

时间:2022-07-23
本文章向大家介绍Python自学成才之路 带有参数的装饰器,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

文章目录

上一节留了点悬念。(上一节) 函数和装饰器都可以添加参数,但是装饰器结构上的区别在于装饰器是否带参数。

第一种:装饰器不带参数

看下面一个案例:

class my_decorate(object):

    def __init__(self, f):
        """
        如果装饰器不带参数,函数需要作为参数传递给这个类的构造器
        """
        print("进入到 __init__")
        self.f = f

    def __call__(self, *args):
        print("进入 __call__")
        self.f(*args)
        print("结束 __call__")

@my_decorate
def myFunction(arg1, arg2):
    print('myFunction arguments:', arg1, arg2)

print("开始调用myfunction")
myFunction("say", "hello")

myFunction("hello", "again")

输出:
进入到 __init__
开始调用myfunction
进入 __call__
myFunction arguments: say hello
结束 __call__
进入 __call__
myFunction arguments: hello again
结束 __call__

注意,这里__init__只被调用了一次,进一步印证了上一节的说法,用类作为装饰器会先得到类实例,然后再去执行类实例。这里执行过程等价于:

myDecorate = my_decorate(myFunction)
myDecorate('say', 'hello')
myDecorate('hello', 'again')

试试打印出myFunction的类型print(type(myFunction)),返回的其实是my_decorate类型,被装饰器修饰的函数最终类型实际上是装饰器本身。

第二种:装饰器带参数

装饰器带参数后结构发生了较大的变化,这时__init__方法中的参数是装饰器的参数而不是函数,使用函数作为参数是在__call__方法中,而且__call__方法需要返回可调用对象。

class my_decorate(object):

    def __init__(self, arg1, arg2, arg3):
        print("进入 __init__()")
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

    def __call__(self, f):
        print("进入 __call__()")

        def wrapped_f(*args):
            print("进入 wrapped_f()")
            print("装饰器参数:", self.arg1, self.arg2, self.arg3)
            f(*args)
            print('执行完函数myfunction')

        return wrapped_f


@my_decorate("hello", "world", 1)
def myFunction(*args):
    if len(args) > 0:
        print('this is myFunction args: %s' % str(args))
    else:
        print('this is myFunctions')


myFunction('hello', 'myfunction')
myFunction()


输出:
进入 __init__()
进入 __call__()
进入 wrapped_f()
装饰器参数: hello world 1
this is myFunction args: ('hello', 'myfunction')
执行完函数myfunction
进入 wrapped_f()
装饰器参数: hello world 1
this is myFunctions
执行完函数myfunction

因为装饰器有了参数,所以这个时候不能在__init__中接收函数作为参数。类比于装饰器无参的时候,当传递函数作为参数时返回的应该是一个可调用对象(在装饰器无参案例中,函数是传递到__init__方法中,等到的是myDecorate实例,myDecorate实例有实现__call__方法,所以是可调用的),而这个时候,函数参数是传递给了__call__方法,所以在__call__方法中返回了wrapped_f这个函数,函数肯定是可调用的。这个过程等价于:

myDecorate = my_decorate("hello", "world", 1)
wrapped_f = myDecorate(f)
wrapped_f('hello', 'myfunction')
wrapped_f()