lru_cache мешает проверке типов, выполняемой single_dispatch

У меня есть декоратор диспетчеризации методов с тремя зарегистрированными функциями . Один отправляет на int, который отлично работает. Второй разослал по кастомному типу, тоже нормально работает. Третий также является пользовательским типом, но класс обернут декоратором lru_cache.

(Чтобы немного усложнить ситуацию, класс создается окольным путем через вызов метода в методе __call__ другого класса.)

@lru_cache(maxsize=None, typed=True)
class QualifiedInterval:
    # stuff that works

Внутри класса Pitch:

@oph_utils.method_dispatch
def augmented(self, other):
    raise NotImplementedError

@augmented.register(int)
def _(self, other):
    return "works fine"


@augmented.register(Interval)
def _(self, other):
    return "works fine too"

@augmented.register(QualifiedInterval)
def _(self, other):
    return "only works if QualifiedInterval class does NOT have lru_cache"

(Происходит намного больше, но это то, что не работает.)

По сути, если у меня есть lru_cache и я передаю QualifiedInterval в функцию, она не отправляется и вызывает NotImplementedError. Если я закомментирую декоратор кеша, он сработает. И ручная проверка типов в REPL показывает один и тот же тип («QualifiedInterval») в любом случае. Я пробовал вызывать команду, которая создала QualifiedInterval, несколькими разными способами и пытался присвоить ее переменной. Все еще не работает. Я пытался выполнить явную проверку типов в расширенной функции. Проверка типов также не проходит там, если включено кэширование.


person Adam Michael Wood    schedule 17.06.2017    source источник


Ответы (1)


Анализ того, что идет не так

В основном - если у меня есть lru_cache и я передаю QualifiedInterval в функцию, она не отправляет

Функция lru_cache возвращает декоратор, обертывающий любой вызываемый объект (включая классы). Поэтому, когда вы применяете lru_cache к классу QualifiedInterval, эта переменная назначается функции-оболочке, а не самому классу.

>>> @lru_cache(maxsize=None, typed=True)
class QualifiedInterval:
    pass

>>> type(QualifiedInterval)
<class 'functools._lru_cache_wrapper'>

Единая отправка работает путем сопоставления типа первого параметра с соответствующий метод. Однако, когда вы передаете экземпляр QualifiedInterval, его тип не соответствует functools._lru_cache_wrapper, поэтому одиночная отправка возвращается к базовому методу (который вызывает Не реализовано.

Решение

Научите одиночную отправку соответствовать фактическому исходному классу (типу) вместо обернутого класса:

@augmented.register(QualifiedInterval.__wrapped__)
def _(self, other):
    return "show now work QualifiedInterval class has an lru_cache"

Обратите внимание на добавление атрибута .__wrapped__, который проходит через функцию-оболочку, чтобы добраться до исходного развернутого класса.

Надеюсь, это все прояснит и покажет путь вперед :-)

person Raymond Hettinger    schedule 17.06.2017
comment
Так что это работает. (Спасибо!) Но почему type(instanceofQualifiedInterval) с одной стороны возвращает QualifiedInterval, а с другой стороны type(instanceofQualifiedInterval) is QualifiedInterval возвращает false. - person Adam Michael Wood; 17.06.2017