第一次尝试翻译《Python装饰器 II :装饰器的参数》

第一次尝试翻译,由于本人水平有限,欢迎指正。
本文也是简单 12 步理解 Python 装饰器推荐继续阅读的两篇文章之一。

原文地址:Python Decorators II: Decorator Arguments
原文标题:
Computing Thoughts
Python Decorators II: Decorator Arguments
by Bruce Eckel
October 19, 2008

以下是翻译内容


摘要:当有参数传递给装饰器时,装饰器的运行机制是明显不同于没有参数传递的时候的


回顾:没有参数传递时的装饰器运行机制
在第一部分,我展示了如何使用没有参数传递情况下的装饰器,之所以我用先用“类”来定义装饰器,是因为我觉得比较容易被理解。
如果我们生成一个没有参数传递的装饰器,那个需要被装饰的函数就会被传递给装饰器,每次装饰器被触发的时候(译者加一句:就是每次被函数调用操作符“()”调用的时候),call()方法都会被调用一次。

class decoratorWithoutArguments(object):

    def __init__(self, f):
        """
        If there are no decorator arguments, the function
        to be decorated is passed to the constructor.
        """
        print ("Inside __init__()")
        self.f = f

    def __call__(self, *args):
        """
        The __call__ method is not called until the
        decorated function is called.
        """
        print ("Inside __call__()")
        self.f(*args)
        print ("After self.f(*args)")

@decoratorWithoutArguments				#译者加注 1
def sayHello(a1, a2, a3, a4):
    print ('sayHello arguments:', a1, a2, a3, a4)

print ("After decoration")				#译者加注 2

print ("Preparing to call sayHello()")
sayHello("say", "hello", "argument", "list")
print ("After first sayHello() call")
sayHello("a", "different", "set of", "arguments")
print ("After second sayHello() call")

任何被传递给装饰器的参数都会被指派给__call__(),因此,输出结果:

Inside __init__()		#译者加注 1的位置被调用,注意,没有参数传递给装饰器
After decoration		#译者加注 2的位置被调用
Preparing to call sayHello()
Inside __call__()
sayHello arguments: say hello argument list
After self.f(*args)
After first sayHello() call
Inside __call__()
sayHello arguments: a different set of arguments
After self.f(*args)
After second sayHello() call

请注意,在装饰器被构建的时候(译者注 1的位置),只有__init__()方法被调用,而__call__()方法是每次通过函数调用操作符“()”调用sayhello函数的时候才被调用。


有参数的装饰器
现在修改上面的例子,看看有参数传递的装饰器的工作机制。

class decoratorWithArguments(object):

    def __init__(self, arg1, arg2, arg3):
        """
        If there are decorator arguments, the function
        to be decorated is not passed to the constructor!
        """
        print ("Inside __init__()")
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3

    def __call__(self, f):
        """
        If there are decorator arguments, __call__() is only called
        once, as part of the decoration process! You can only give
        it a single argument, which is the function object.
        """
        print ("Inside __call__()")
        def wrapped_f(*args):
            print ("Inside wrapped_f()")
            print ("Decorator arguments:", self.arg1, self.arg2, self.arg3)
            f(*args)
            print ("After f(*args)")
        return wrapped_f

@decoratorWithArguments("hello", "world", 42)	#译者加注 1
def sayHello(a1, a2, a3, a4):
    print ('sayHello arguments:', a1, a2, a3, a4)

print ("After decoration")

print ("Preparing to call sayHello()")
sayHello("say", "hello", "argument", "list")
print ("after first sayHello() call")
sayHello("a", "different", "set of", "arguments")
print ("after second sayHello() call")

从输出可以看出明显的不同。

Inside __init__()	#译者加注 1 处调用运行
Inside __call__()	#译者加注 1 处调用运行
After decoration
Preparing to call sayHello()
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: say hello argument list
After f(*args)
after first sayHello() call
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: a different set of arguments
After f(*args)
after second sayHello() call

装饰器的调用流程发生了明显的变化,当构建装饰器的时候,首先调用构造器__init__(),然后立即调用__call__(),call()方法接收输入的参数(即被装饰函数sayHello),然后返回真正的装饰函数wrapped_f。注意,call()方法只被调用这一次,而后,每次对装饰器的调用,实际上都是调用那个装饰函数warpped_f。
虽然看起来很容易理解,但还是要注意不同情况下编写装饰器类的区别。

带参数的装饰器函数
最后,学习更复杂的装饰器函数的实现方法,这种实现方法,一次性的定义所有装饰器所设计的内容。

扫描二维码关注公众号,回复: 4796417 查看本文章
def decoratorFunctionWithArguments(arg1, arg2, arg3):
    def wrap(f):
        print "Inside wrap()"
        def wrapped_f(*args):
            print "Inside wrapped_f()"
            print "Decorator arguments:", arg1, arg2, arg3
            f(*args)
            print "After f(*args)"
        return wrapped_f
    return wrap

@decoratorFunctionWithArguments("hello", "world", 42)
def sayHello(a1, a2, a3, a4):
    print 'sayHello arguments:', a1, a2, a3, a4

print "After decoration"

print "Preparing to call sayHello()"
sayHello("say", "hello", "argument", "list")
print "after first sayHello() call"
sayHello("a", "different", "set of", "arguments")
print "after second sayHello() call"

输出结果如下:

Inside wrap()
After decoration
Preparing to call sayHello()
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: say hello argument list
After f(*args)
after first sayHello() call
Inside wrapped_f()
Decorator arguments: hello world 42
sayHello arguments: a different set of arguments
After f(*args)
after second sayHello() call

装饰器的返回值必须是一个“用于包装被装饰函数”的函数,也就是说,在每次“装饰”的时候,python都会调用那个“用于包装被包装函数”的函数(wrap),并把“被装饰函数”传递给它,这也正是我们有三层函数的原因:最内层的那个函数才是真正的那个“装饰函数”。
因为python的“闭包”特性,wrap函数能够访问到装饰器的参数arg1、arg2和arg3,而不必像“类装饰器”中那样,将参数存储在类的属性中。然而,根据“明确要比模糊强”这个概念,虽然函数版本的装饰器定义更加简明扼要,但还是类版本的装饰器更加容易理解、修改及维护。

猜你喜欢

转载自blog.csdn.net/steventian72/article/details/85871996