装饰装饰:试着理解它

时间:2011-05-10 15:36:56

标签: python decorator

我正在尝试了解装饰装饰器,并想尝试以下内容:

假设我有两个装饰器并将它们应用于函数hello:

def wrap(f):
    def wrapper():
        return " ".join(f())
    return wrapper


def upper(f):
    def uppercase(*args, **kargs):
        a,b = f(*args, **kargs)
        return a.upper(), b.upper()
    return uppercase

@wrap
@upper
def hello():
    return "hello","world"

print hello()

然后我必须开始为其他功能添加其他装饰器, 但一般情况下,wrap装饰器将“包裹所有这些”

def lower(f):
    def lowercase(*args, **kargs):
        a,b = f(*args, **kargs)
        return a.lower(), b.lower()
    return lowercase

@wrap
@lower
def byebye():
    return "bye", "bye"

现在我该如何编写一个装饰器,我可以装饰我的下装饰器和上装饰器:

@wrap
def lower():
    ...

@wrap
def upper():
    ...

仅通过以下方式获得与上述相同的结果:

@upper
def hello():
    ...

@lower
def byebye():
    ...

2 个答案:

答案 0 :(得分:23)

def upper(f):
    @wrap
    def uppercase(*args, **kargs):
        a,b = f(*args, **kargs)
        return a.upper(), b.upper()
    return uppercase

Python中的装饰器

 @foo
 def bar(...): ...

等同于

 def bar(...): ...
 bar = foo(bar)

你想获得

的效果
@wrap
@upper
def hello(): ....

hello = wrap(upper(hello))

所以wrap应该在upper返回值上调用:

def upper_with_wrap(f):
   def uppercase(...): ...
   return wrap(uppercase)

这也等同于在该函数上应用装饰器:

def upper_with_wrap(f):
   @wrap
   def uppercase(...): ...
   # ^ equivalent to 'uppercase = wrap(uppercase)'
   return uppercase

答案 1 :(得分:8)

这是一个用装饰器装饰装饰器的通用(并且有点复杂)解决方案(耶!)。

# A second-order decorator
def decdec(inner_dec):
    def ddmain(outer_dec):
        def decwrapper(f):
            wrapped = inner_dec(outer_dec(f))
            def fwrapper(*args, **kwargs):
               return wrapped(*args, **kwargs)
            return fwrapper
        return decwrapper
    return ddmain

def wrap(f):
    def wrapper():
        return " ".join(f())
    return wrapper


# Decorate upper (a decorator) with wrap (another decorator)
@decdec(wrap)
def upper(f):
    def uppercase(*args, **kargs):
        a,b = f(*args, **kargs)
        return a.upper(), b.upper()
    return uppercase

@upper
def hello():
    return "hello","world"

print hello()