Python的@符号 --decorator

来源:互联网 发布:员工签到软件 编辑:程序博客网 时间:2024/05/27 20:33
Python语言中的@符号有比较特殊的含义,而且是一种比较稀有的特性,初学者往往不容易理解,这里就其做一些解释。 首先看一段代码:
>>> def now():...     print '2013-12-25'

假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

我们要定义一个能打印日志的decorator,可以定义如下:

def log(func):    def wrapper(*args, **kw):        print 'call %s():' % func.__name__        return func(*args, **kw)    return wrapper
@logdef now():    print '2013-12-25'

调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:

>>> now()call now():2013-12-25

把@log放到now()函数的定义处,相当于执行了语句:now= log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。

decorator类

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)" @decoratorWithoutArgumentsdef 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__()
After decoration
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

注意,decoratorWithoutArguments的构造器(constructor)在函数的decoration处执行。由于我们可以在__init__()里面调用f(),它意味着在调用decorator前就完成了f()的创建。另外需注意,decorator构造器接收到decorated的函数对象。你将在构造器中得到函数对象,之后在__call__()方法中进行使用(当使用类时,decoration和调用是两个泾渭分明的阶段,这也是我为何说它更简单、更强大的原因)。
当decorated之后再调用sayHello(),它的行为就完全不一样了:不再用原来的代码而开始调用decoratorWithoutArguments.__call__()方法。原因是decoration过程是将decorator结果取代原先的函数--在我们的例子中,decoratorWithoutArguments对象取代了aFunction。

含有参数的decorators

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)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__()
Inside __call__()
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

现在decoration方法调用构造器,然后就马上调用__call__(),后者只能包含一个参数(函数对象)且返回替代原有函数的decorated函数对象。注意当前decoration期间__call__()仅被调用一次,此后从__call__()返回的decorated函数就可以在实际调用中使用了。
虽然这种机制有一定合理性—构造器在这里可获取decorator参数,但__call__()对象不能再作为decorated函数使用了。因此你必须使用__call__()执行decoration—可能第一次遇到这种与无参情况截然不同的方式你会比较吃惊,何况还必须编写和无参decorator完成不同的代码。

含decorator参数的decorator函数

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

参考:

http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819879946007bbf6ad052463ab18034f0254bf355000

http://blog.csdn.net/beckel/article/details/3945147

原创粉丝点击