Почему декораторы Python не могут быть объединены в цепочку через определения?

Почему arn't следующие два эквивалентные сценария?

(Взятый от другого вопроса: Понимание Декораторов Python)

def makebold(fn):
    def wrapped():
        return "" + fn() + ""
    return wrapped

def makeitalic(fn):
    def wrapped():
        return "" + fn() + ""
    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

print hello() ## returns hello world

и с титулованным декоратором:

def makebold(fn):
    def wrapped():
        return "" + fn() + ""
    return wrapped

@makebold
def makeitalic(fn):
    def wrapped():
        return "" + fn() + ""
    return wrapped

@makeitalic
def hello():
    return "hello world"

print hello() ## TypeError: wrapped() takes no arguments (1 given)

Почему я хочу знать? Я записал a retry декоратор для ловли исключений MySQLdb - если исключение является переходным (например, Тайм-аут) оно вспомнит функцию после сна немного.

Я также получил a modifies_db декоратор, который заботится о некотором связанном с кэшем обслуживании. modifies_db украшен retry, таким образом, я предположил, что все функции украсили modifies_db также повторил бы неявно. Где я шел не так, как надо?

8
задан Community 23 May 2017 в 12:03
поделиться

3 ответа

Проблема со вторым примером состоит в том, что

@makebold
def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

пытается украсить Maseitalic , декоратор, а не , завернутый , функция, которая возвращается.

Вы можете делать то, что я думаю, что вы намереваетесь с чем-то вроде этого:

def makeitalic(fn):
    @makebold
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

здесь Maseitalic использует Makebold , чтобы украсить .

9
ответ дан 5 December 2019 в 17:37
поделиться

Причина в том, что обернуты () внутри Мягбальда не принимают никаких аргументов.

Когда вы используете такой декоратор, он может привести к некоторым вопросам, я опубликую пример того, как добиться того, хотя вы хотите, чтобы я даю мне минутку.

Вот рабочий пример того, что вам нужно.

def makebold(rewrap=False):
    if rewrap:
        def inner(decorator):
            def rewrapper(func):
                def wrapped(*args, **kwargs):
                    return "<b>%s</b>" % decorator(func)(*args,**kwargs)
                return wrapped
            return rewrapper
        return inner

    else:
        def inner(func):
            def wrapped(*args, **kwargs):
                return "<b>%s</b>" % func(*args, **kwargs)    
            return wrapped
        return inner

@makebold(rewrap=True)
def makeitalic(fn):
    def wrapped(*args, **kwargs):
        return "<i>%s</i>" % fn(*args, **kwargs)
    return wrapped

@makeitalic
def hello():
    return "hello world"

@makebold()
def hello2():
    return "Bob Dole"    

if __name__ == "__main__":
    print hello()   
    print hello2()

MakeBold вроде уродливая, но он показывает, как написать декоратор, который может при желании обернуть другой декоратор.

Вот выход из вышеуказанного скрипта:

<b><i>hello world</i></b>
<b>Bob Dole</b>

Обратите внимание, что MakeBold является единственным рекурсивным декоратором. Также обратите внимание на тонкую разницу в использовании: @makebold () против @makeitalic .

1
ответ дан 5 December 2019 в 17:37
поделиться

Проблема заменяет «Макиаталогический» (которая принимает один аргумент) с «обернутой» - в «Мяггильском», который принимает нулевые аргументы.

Использование * args, ** Kwargs , чтобы пройти на аргументы дальше вниз по цепочке:

def wrapped(*args, **kwargs):
    return "<b>" + fn(*args, **kwargs) + "</b>"
0
ответ дан 5 December 2019 в 17:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: