Я столкнулся со странным поведением functools.update_wrapper
: оно перезаписывает __dict__
объекта-оболочки на объект обернутого объекта - что может затруднить его использование при вложении декораторов.
В качестве простого примера предположим, что мы пишем класс декоратора, который кэширует данные в памяти, и другой класс декоратора, который кэширует данные в файл.Следующий пример демонстрирует это (я сделал пример кратким и пропустил всю логику кэширования, но я надеюсь, что он демонстрирует вопрос):
import functools
class cached:
cache_type = 'memory'
def __init__(self, fcn):
super().__init__()
self.fcn = fcn
functools.update_wrapper(self, fcn, updated=())
def __call__(self, *args):
print("Retrieving from", type(self).cache_type)
return self.fcn(*args)
class diskcached(cached):
cache_type = 'disk'
@cached
@diskcached
def expensive_function(what):
print("expensive_function working on", what)
expensive_function("Expensive Calculation")
Этот пример работает как задумано - его вывод равен
Retrieving from memory
Retrieving from disk
expensive_function working on Expensive Calculation
Однако это заняло у меня много времени - сначала я не включил аргумент updated = () в вызов functools.update_wrapper.Но если это не учитывать, то вложение декораторов не работает - в этом случае вывод будет
Retrieving from memory
expensive_function working on Expensive Calculation
Т.е. внешний декоратор напрямую вызывает самую внутреннюю упакованную функцию.Причина (которая заняла у меня некоторое время, чтобы понять) заключается в том, что functools.update_wrapper
обновляет атрибут __dict__
обертки до атрибута __dict__
обернутого аргумента, который закорачивает внутренний декоратор, если только один не добавляетupdated=()
аргумент.
Мой вопрос: это поведение предназначено и почему?(Python 3.7.1)