您的当前位置:首页Python 装饰器

Python 装饰器

2024-12-14 来源:哗拓教育

按照python官方文档,函数定义可以被一个或者多个decorator包起来,解释如下:

@f1(arg)
@f2
def func(): pass

等同于

def func(): pass
func = f1(arg)(f2(func))

而且:Decorator expressions are evaluated when the function is defined, in the scope that contains the function definition


Example 1. 无参数装饰器

def hello(fn):
    def wrapper():
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper
 
@hello
def foo():
    print "i am foo"

解释

  • 编译器做如下转换:
@hello
def foo     # ===> hello(foo)
  • foo = hello(foo) = wrapper
  • foo此时仅仅是一个函数,没有被调用,foo()才会调用

这种装饰器,仅仅将在已有函数的基础之上,附加额外的装饰功能。
到底什么时候调用,由使用者决定


Example 2. 带参数装饰器

def hello(dummy=""):
    def wrapper(fn):
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper
 
@hello("")
def foo():
    print "i am foo"

解释

  • 编译器做如下转换:
@hello("")
def foo     # ===> hello("")(foo)
  • foo = hello("")(foo)
  • hello("") = wrapper
  • foo = wrapper(foo)
  • 此时编译器已经执行wrapper函数
  • 由于wrapper没有返回值,foo=None

这种装饰器就不一样了,函数定义期间就被赋予执行的行为
设想:这可以被用来做某些初始化动作

从上面的例子分析可以看到,被decorator的函数其实已经是另外一个函数了,对于最前面那个hello.py的例子来说,如果你查询一下foo.__name__的话,你会发现其输出的是“wrapper”,而不是我们期望的“foo”,这会给我们的程序埋一些坑。所以,Python的functool包中提供了一个叫wrap的decorator来消除这样的副作用

from functools import wraps
def hello(fn):
    @wraps(fn)
    def wrapper():
        print "hello, %s" % fn.__name__
        fn()
        print "goodby, %s" % fn.__name__
    return wrapper
 
@hello
def foo():
    '''foo help doc'''
    print "i am foo"
    pass
 
foo()
print foo.__name__ #输出 foo
print foo.__doc__  #输出 foo help doc

应用1. 给函数调用做缓存

from functools import wraps
def memo(fn):
    cache = {}
    miss = object()
 
    @wraps(fn)
    def wrapper(*args):
        result = cache.get(args, miss)
        if result is miss:
            result = fn(*args)
            cache[args] = result
        return result
 
    return wrapper
 
@memo
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

应用2. 注册回调函数

class MyApp():
    def __init__(self):
        self.func_map = {}
 
    def register(self, name):
        def func_wrapper(func):
            self.func_map[name] = func
            return func
        return func_wrapper
 
    def call_method(self, name=None):
        func = self.func_map.get(name, None)
        if func is None:
            raise Exception("No function 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')
显示全文