декорирование декораторов: попытайтесь понять это - PullRequest
20 голосов
/ 10 мая 2011

Я пытаюсь понять, как декорировать декораторы, и хотел попробовать следующее:

Допустим, у меня есть два декоратора и я применил их к функции привет:

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()

ТогдаЯ должен начать добавлять другие декораторы для других функций, но в целом декоратор обтекания "обернет их всех"

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 ]

23 голосов
/ 10 мая 2011
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(): ....

1012 * т.е. *

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
8 голосов
/ 10 мая 2011

Вот общее (и немного запутанное) решение для украшения декораторов декораторами (Да!).

# 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()
...