Вопрос по python – Декорирование рекурсивных функций в python

8

Мне трудно понять, как работает декорированная рекурсивная функция. Для следующего фрагмента:

def dec(f):
    def wrapper(*argv):
        print(argv, 'Decorated!')
        return(f(*argv))
    return(wrapper)

def f(n):
    print(n, 'Original!')
    if n == 1: return(1)
    else: return(f(n - 1) + n)

print(f(5))
print

dec_f = dec(f)
print(dec_f(5))
print

f = dec(f)
print(f(5))

Выход:

(5, 'Original!')
(4, 'Original!')
(3, 'Original!')
(2, 'Original!')
(1, 'Original!')
15

((5,), 'Decorated!')
(5, 'Original!')
(4, 'Original!')
(3, 'Original!')
(2, 'Original!')
(1, 'Original!')
15

((5,), 'Decorated!')
(5, 'Original!')
((4,), 'Decorated!')
(4, 'Original!')
((3,), 'Decorated!')
(3, 'Original!')
((2,), 'Decorated!')
(2, 'Original!')
((1,), 'Decorated!')
(1, 'Original!')
15

Первый из них печатает f (n), поэтому, естественно, он печатает «Оригинал». каждый раз, когда f (n) вызывается рекурсивно.

Второй выводит def_f (n), поэтому, когда n передается в оболочку, он вызывает f (n) рекурсивно. Но сама обертка не является рекурсивной, так что только один «Декорированный» печатается.

Третий меня озадачивает, это то же самое, что и использование декоратора @dec. Почему оформленный f (n) также вызывает обертку пять раз? Мне кажется, что def_f = dec (f) и f = dec (f) - это просто два ключевых слова, связанных с двумя одинаковыми функциональными объектами. Есть ли что-то еще, когда декорированная функция имеет то же имя, что и неокрашенная?

Спасибо!

ссылка на оригиналf функция все еще существует, таким образом, один из них называется. Когда вы делаетеf = dec(f), вы всегда будете вызывать новую функцию. И новая функция будет вызывать оригинал. JBernardo
decorator может быть неправильный термин для использования здесь, так как вы никогда не применяете декоратор к функции. Ваш последний тестf = dec(f) почти (если не совсем) так же, как@dec def f Haldean Brown

Ваш Ответ

5   ответов
0

one до или после другой функции, мы можем избежать этогоseveral times имитирующие декораторы с рекурсивными функциями.

Например:

def timing(f):
    def wrapper(*args):
       t1 = time.clock();
       r = apply(f,args)
       t2 = time.clock();
       print"%f seconds" % (t2-t1)
       return r
    return wrapper

@timing
def fibonacci(n):
    if n==1 or n==2:
      return 1
    return fibonacci(n-1)+fibonacci(n-2)

r = fibonacci(5)
print "Fibonacci of %d is %d" % (5,r)

Производит:

0.000000 seconds
0.000001 seconds
0.000026 seconds
0.000001 seconds
0.000030 seconds
0.000000 seconds
0.000001 seconds
0.000007 seconds
0.000045 seconds
Fibonacci of 5 is 5

Мы можем смоделировать декоратор, чтобы заставить только один пролог / эпилог как:

r = timing(fibonacci)(5)
print "Fibonacci %d of is %d" % (5,r)

Который производит:

0.000010 seconds
Fibonacci 5 of is 5
5

второй помещает декорированную версию f с именем dec_f в глобальную область видимости. Вызывается dec_f, чтобы печатать "Decorated!", Но внутри функции f, переданной в dec, вы вызываете сам f, а не dec_f. имя f ищется и находится в глобальной области видимости, где оно все еще определено без оболочки, поэтому с этого момента вызывается только f.

в примере 3re вы назначаете декорированную версию имени f, поэтому, когда внутри функции f ищется имя f, она ищет в глобальной области видимости f, которая теперь является декорированной версией.

Спасибо! Это то, что я искал. Таким образом, проблема заключается в выражении return (f (n-1) + n) в def f, где f (n-1) теперь является новым dec (f). jianglai
1

f, какой питон смотрит в области видимости.

До утвержденияf = dec(f), f все еще связан с развернутой функцией, так что вызывается.

5

тебя есть

f = dec(f)

то, что вы делаете, привязывает имяf к возвращаемому значениюdec(f), В таком случае,f больше не относится к исходной функции. Исходная функция все еще существует и вызывается новойf, но у вас нетnamed ссылка на оригинальную функцию больше.

0

def dec(func):
    def wrapper(*argv):
        print(argv, 'Decorated!')
        return(func(*argv))
    return(wrapper)

def f(n):
    print(n, 'Original!')
    if n == 1: return(1)
    else: return(f(n - 1) + n)

print(f(5))
print

dec_f = dec(f)
print(dec_f(5))
print

f = dec(f)
print(f(5))

Я думаю, что это могло бы сделать вещи немного более понятными, функция-обертка фактически закрывает объект func от ограничивающей области. Таким образом, каждый вызов func внутри оболочки будет вызывать оригинальный f, а рекурсивный вызов внутри f будет вызывать декорированную версию f.

Вы можете увидеть это, просто распечатавfunc.__name__ внутри обертки иf.__name__ внутри функцииf

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