Двойная упаковка Фибоначчи - PullRequest
       11

Двойная упаковка Фибоначчи

2 голосов
/ 26 сентября 2019

Я пытаюсь использовать две разные обертки для рекурсивной функции Фибоначчи, чтобы: 1) считать количество рекурсий, 2) запоминать вычисленные значения, чтобы уменьшить необходимые вычисления.

Поскольку каждая функция обертывания создает новую функциюатрибут созданной функции, после переноса снова я не могу получить к нему доступ.Есть ли способ получить к нему доступ, не выполнив ни одной функции обертывания с обоими эффектами?

def count(f):
    def f1(*args):
        f1.counter += 1
        return f(*args)
    f1.counter = 0
    return f1

def memoize(f):
    def f1(*args):
        if not args in f1.memo:
            f1.memo[args] = f(*args)
        return f1.memo[args]
    f1.memo = {}
    return f1

@memoize
@count
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

После этого я могу получить доступ к fib.memo, но не к fib.counter, и наоборот, если я оберну fib с memoize до этогососчитать.

1 Ответ

0 голосов
/ 26 сентября 2019

Этот вопрос был первоначально закрыт как дубликат к Как создать цепочку декораторов функций? , где правильно указано, что использование functools.wraps для декорации цепочек может помочь путем копирования всех атрибутовобернутая функция к функции-обертке.

Однако я снова открываю этот вопрос, потому что использование functools.wraps не будет работать полностью в вашем случае, поскольку ваш атрибут counter является неизменным целым числом, поэтому наличие wraps копирует атрибут counter в оболочку, фактически копирует его первоначальное значение 0 в оболочку, и последующие изменения атрибута counter функции-оболочки не будут отражены в атрибуте counter оболочки, которыйВот почему:

from functools import wraps

def count(f):
    @wraps(f)
    def f1(*args):
        f1.counter += 1
        return f(*args)
    f1.counter = 0
    return f1

def memoize(f):
    @wraps(f)
    def f1(*args):
        if not args in f1.memo:
            f1.memo[args] = f(*args)
        return f1.memo[args]
    f1.memo = {}
    return f1

@memoize
@count
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(4))
print(fib.memo)
print(fib.counter)

неправильно выводит:

3
{(1,): 1, (0,): 0, (2,): 1, (3,): 2, (4,): 3}
0

Чтобы исправить это, вы должны инициализировать counter с изменяемым объектом.Поскольку Python изначально не имеет изменяемого целого числа, вы можете создать собственный класс, который вместо этого будет заключен в целое число:

class Int:
    def __init__(self, value=0):
        self.value = value

def count(f):
    @wraps(f)
    def f1(*args):
        f1.counter.value += 1
        return f(*args)
    f1.counter = Int()
    return f1

, чтобы:

print(fib(4))
print(fib.memo)
print(fib.counter.value)

правильно выводил:

3
{(1,): 1, (0,): 0, (2,): 1, (3,): 2, (4,): 3}
5
...