装饰器就是对已有函数添加功能,又可以不去修改原有的函数的方法。它的实现依赖于高阶函数(HOF)。这里主要参考着两篇文章来学习

  1. 理解 Python 装饰器看这一篇就够了
  2. Python修饰器的函数式编程
  3. Python Decorator Library

1. 不带参数的简单装饰器

  • 使用wrapper(*args, **kwargs)来接受decoratee的参数

2. 带参数的装饰器

  • 需要两层嵌套函数定义

3. 类装饰器

  • 使用类中定义的call(self, *args, **kwargs)方法来调用decoratee
  • 需要init来初始化需要装饰的函数

4. functools.wraps

  • 用来更新decoratee的元信息
  • TODO 类装饰器中使用functools.wraps

5. 内置装饰器

  • @staticmethod
  • @classmethod
  • @property

6. 装饰器的顺序

  • @a @b @c def f(): pass
  • =>a(b(c(f)))

7. 通过装饰器来修改函数的调用参数,此时需要函数接受可变数量的参数

  1. 通过给*args添加位置元素
  2. 通过**kwargs添加命名参数
  3. 通过预先的约定在decoratee中有某个参数,然后在decorator中去修改

8. decorator经典的用法

  1. 给函数调用做缓存
  2. 注册回调函数
  3. 给函数打日志 。。。

In [6]:
# 不带参数的简单装饰器
def use_logging(func):
    def wrapper(*args, **kwargs):
        print("Hello,", func.__name__)
        func(*args, **kwargs)
    return wrapper

@use_logging
def foo():
    print("I am Longshan DU")
    

foo()


Hello, foo
I am Longshan DU

In [10]:
# 带参数的装饰器
def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == 'warn':
                print("The log level of %s is 'warn'" %func.__name__)
            elif level == 'info':
                print("The log level of %s is 'info'" %func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@use_logging(level='warn')
def foo():
    print("I am Longshan DU")

foo()


The log level of foo is 'warn'
I am Longshan DU

In [13]:
# 类装饰器
class Foo(object):
    def __init__(self, func):
        self._func = func
    def __call__(self, *args, **kwargs):
        print('Class decorator running')
        self._func(*args, **kwargs)
        print('Class decorator ending')
@Foo
def bar(name):
    print('I am', name)
    
bar('Longshan')


Class decorator running
I am Longshan
Class decorator ending

In [22]:
# functools.wraps
import functools

def use_logging(level):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if level == 'warn':
                print("The log level of %s is 'warn'" %func.__name__)
            elif level == 'info':
                print("The log level of %s is 'info'" %func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@use_logging(level='warn')
def foo():
    print("I am Longshan DU")

foo()
print(foo.__name__)


The log level of foo is 'warn'
I am Longshan DU
wrapper

In [31]:
# 类装饰器
import functools

class Foo(object):
    def __init__(self, func):
        self._func = func
        functools.wraps(self._func)
    def __call__(self, *args, **kwargs):
        print('Class decorator running')
        
        self._func(*args, **kwargs)
        print('Class decorator ending')
@Foo
def bar(name):
    print('I am', name)

bar('Longshan')
print(bar.__class__.__name__)


Class decorator running
I am Longshan
Class decorator ending
Foo

In [51]:
# 通过*args修改decoratee的参数
def decorate_A(func):
    def wrapper(*args, **kwargs):
        hello = 'Hello'
        args = (hello,) + args
        return func(*args, **kwargs)
    return wrapper

@decorate_A
def print_message_A(*name):
    print(*name)

print_message_A('Longshan')

# 通过\*\*kwargs修改的decoratee的参数
def decorate_B(func):
    def wrapper(*args, **kwargs):
        kwargs['hello'] = 'Hello'
        return func(*args, **kwargs)
    return wrapper

@decorate_B
def print_message_B(*args, **kwargs):
    print(kwargs['hello'], *args)
    
print_message_B('Longshan DU')

# 约定好参数,直接修改
def decorate_C(func):
    def wrapper(*args, **kwargs):
        hello = 'Hello, world!'
        return func(hello)
    return wrapper

@decorate_C
def print_message_C(hello):
    print(hello)
    
print_message_C('Longshan')


Hello Longshan
Hello Longshan DU
Hello, world!

In [1]:
# 给函数做缓存
from functools import wraps

def memo(func):
    cache = {}
    miss = object()
    def wrapper(*args, **kwargs):
        result = cache.get(func, miss)
        if result is miss:
            result = func(*args, **kwargs)
            cache[args] = result
        return result
    return wrapper

@memo
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

print(fib(30))


832040

In [8]:
# 注册回调函数
class MyApp():
    def __init__(self):
        self.func_map = {}
    
    def register(self, name):
        def wrapper(func):
            self.func_map[name] = func
            return func
        return wrapper
    
    def call_method(self, name=None):
        func = self.func_map.get(name, None)
        if func is None:
            raise Exception("No functin registered against - " + str(name))
        return func()

app = MyApp()

@app.register('/')
def main_page_func():
    return 'This is the main page.'

@app.register('/next_page')
def next_page_func():
    return 'This is the next page.'

print(app.call_method('/'))
print(app.call_method('/next_page'))


This is the main page.
This is the next page.

In [ ]: