Python 装饰器

  装饰器本质上是一个 Python 函数,它可以让其他函数在不变动代码的前提下增加额外功能,装饰器的返回值也是一个函数对象。

定义函数

>>> def foo():
...     print("hello")
...
>>> foo
<function foo at 0x00000000035EC268>
>>> foo()
hello

  定义了一个函数,函数名 foo,也可以把 foo 理解为变量名,该变量指向一个函数对象。调用函数只需要在函数名后加上 () 并传递参数(如果函数定义的时候有参数的话)。

函数作为返回值

  在 Python 中,一切皆为对象,函数也不例外,可以作为其它函数的返回值。

>>> def foo():
...     print("hello")
...
>>> def bar():
...     return foo
...
>>> bar()
<function foo at 0x00000000035EC268>
>>> bar()()
hello

  调用函数 bar() 的返回值是一个函数对象 ,变量 foo 指向的对象与 bar() 的返回值是同一个对象。

函数作为参数

>>> def foo(num):
...     return num + 1
...
>>> def bar(func):
...     return func(2)
...
>>> bar(foo)
3

  函数 bar() 接收一个函数对象 foo 作为参数。

函数嵌套

  函数可以定义在另一个函数中,作为嵌套函数存在。

>>> def outer():
...     x = 1
...     def inner():
...         print(x)
...     inner()
...
>>> outer()
1

闭包

>>> def outer(x):
...   def inner():
...     print(x)
...   return inner
...
>>> closure = outer(1)
>>> closure
<function outer.<locals>.inner at 0x0000000003ED9840>
>>> closure()
1

  嵌套函数 inner 不再直接在函数里被调用,而是作为返回值。这里的 closure 就是一个闭包,本质上还是函数。

装饰器

  先来看一个简单的例子:

def foo():
    print("i am foo.")

  现在有一个新的需求,希望可以记录下函数的执行日志,添加代码:

def foo():
    print("i am foo.")
    logging.info("foo is running.")

  如果其他函数也有类似需求,该怎么做呢?我们可以定义一个函数专门处理日志,然后再处理业务代码:

def use_logging(func):
    logging.info("{} is running.".format(func.__name__))
    func()

def foo():
    print("i am foo.")

use_logging(foo)

  但是这样的话,函数 foo 的调用方式就改变了。那有没有更好的方式呢?有,就是装饰器。

简单装饰器

def use_logging(func):      
    def wrapper(*args,**kwargs):
        logging.info("{} is running.".format(func.__name__))
        func(*args,**kwargs)
    return wrapper

def foo():
    print("i am foo.")

foo = use_logging(foo)
foo()

  foo 函数的代码没有变动,只是给 foo 变量重新赋值了,指向一个新的函数对象。

  这里的 use_logging 函数其实就是一个装饰器,装饰器将函数作为参数传递给另一个函数,外部函数返回内部函数对象。

  @符号是装饰器的语法糖,在定义函数时使用,省去了重新赋值操作。

def use_logging(func):
    def wrapper(*args,**kwargs):
        logging.info("{} is running.".format(func.__name__))
        func(*args,**kwargs)
    return wrapper

@use_logging    # 等效于 foo = use_logging(foo)
def foo():
    print("i am foo.")

foo()

带参数的装饰器

  在上面的装饰器调用中,@use_logging 的参数是执行业务的函数 foo 。装饰器的语法允许我们在调用时提供其他参数,比如 @decorator(func)。

def use_logging(level):
    def decorator(func):
        def wrapper(*args,**kwargs):
            if level == "info":
                logging.info("{} is running.".format(func.__name__))
            func(*args,**kwargs)
        return wrapper
    return decorator

@use_logging("info")
def foo(name="foo"):
    print("i am {}.".format(name))

foo()

类装饰器

class Test(object):
    def __init__(self,func):
        self.func = func

    def __call__(self,*args,**kwargs):
        print("{} is running.".format(self.func.__name__))
        self.func(*args,**kwargs)

@Test  # 等效于 test = Test(test) 
def test(): 
    print("This is text.")

test()

  实例化对象时使用 __init__ 方法接收需要装饰的函数,定义 __call__ 方法运行装饰的函数代码。

内置装饰器

  @staticmathod,@classmathod,@property

装饰器顺序

@desc1(args)
@desc2
@desc3
def func():
    pass

等效于: f = desc1(args)(desc2(desc3(func)))
posted @ 2019-05-16 10:30  PIPO2  阅读(145)  评论(0编辑  收藏  举报