python decorator

decorator(装饰器)绝对是Python学习道路上的一个拦路虎。想要理解它,先看两个概念。

nested function(内嵌函数)

def helper(num):
    def is_even(arg):
        return arg % 2 == 0
    if is_even(num):
        print(num, 'is even')
    else:
        print(num, 'is odd')

类似于函数局部变量,内嵌函数就是函数的局部函数。

Python的内嵌函数有个特点,它可以访问自身作用域外围的变量。

def helper(num):
    def is_even():
        return num % 2 == 0
    if is_even():
        print(num, 'is even')
    else:
        print(num, 'is odd')

内嵌函数is_even可以直接访问外围参数num。

函数可以作为返回值

def helper(type):
    def helper_lower(arg):
        return arg.lower()
    def helper_upper(arg):
        return arg.upper()
    if type == 'lower':
        return helper_lower
    else:
        return helper_upper

mylower = helper('lower')
print(mylower)
print(mylower('HELLO WORLD'))

myupper = helper('upper')
print(myupper)
print(myupper('hello world'))

<function helper.<locals>.helper_lower at 0x106aee598>
hello world
<function helper.<locals>.helper_upper at 0x106aee730>
HELLO WORLD

 

现在回到装饰器。

装饰器的作用是,在不改变函数原有实现的基础上,给函数增加新的功能。

举个例子:

def decorator(func):
    print('before the function runs...')
    func()
    print('after the function runs...')
    
def hello():
    print('hello, i am foo')

现在执行decorator(hello),输出如下:

before the function runs...
hello, i am foo
after the function runs...

 

但是有个问题,为了这个新功能,用户需要把所有调用hello的地方换成decorator(hello)。

可以通过返回内嵌函数的方式解决:

def decorator(func):
    def wrapper():
        print('before the function runs...')
        func()
        print('after the function runs...')
    return wrapper

def hello():
    print('hello, i am foo')
    
hello = decorator(hello)
hello()

如果原先的函数hello有参数怎么办呢?

def decorator(func):
    def wrapper(name):
        print('before the function runs...')
        func(name)
        print('after the function runs...')
    return wrapper

def hello(name):
    print('hello, i am', name)
    
hello = decorator(hello)
hello('logan')

参数传递细节

hello('logan')

decorator(hello)('logan')

wrapper('logan')

通用版本的decorator如下:

def decorator(func):
    def wrapper(*args, **kw):
        print('before the function runs...')
        func(*args, **kw)
        print('after the function runs...')
    return wrapper

python为了简化hello = decorator(hello),使用了@符号。

def decorator(func):
    def wrapper(*args, **kw):
        print('before the function runs...')
        func(*args, **kw)
        print('after the function runs...')
    return wrapper

@decorator
def hello(name):
    print('hello, i am', name)

hello('logan')

 

另一种实现decorator的方法是functools.wraps

import functools

def decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('before the function runs...')
        func(*args, **kw)
        print('after the function runs...')
    return wrapper

@decorator
def hello(name):
    print('hello, i am', name)

hello('logan')

 

参考资料:

How to make a chain of function decorators?

posted @ 2017-08-05 15:08  Sawyer Ford  阅读(165)  评论(0编辑  收藏  举报