добавьте украсить функцию к классу

У меня есть украшенная функция (упрощенная версия):

class Memoize:
    def __init__(self, function):
        self.function = function
        self.memoized = {}
    def __call__(self, *args, **kwds):
        hash = args
        try:
            return self.memoized[hash]
        except KeyError:
            self.memoized[hash] = self.function(*args)
            return self.memoized[hash]


@Memoize
def _DrawPlot(self, options):
    do something...

теперь я хочу добавить этот метод к pre-esisting классу.

ROOT.TChain.DrawPlot = _DrawPlot

когда я называю этот метод:

chain = TChain()
chain.DrawPlot(opts)

Я добрался:

self.memoized[hash] = self.function(*args)
TypeError: _DrawPlot() takes exactly 2 arguments (1 given)

почему это не распространяет сам?

6
задан Ruggero Turra 29 March 2010 в 09:27
поделиться

1 ответ

Проблема в том, что вы определили свой собственный вызываемый класс, а затем попытались использовать его как метод. Когда вы используете функцию как атрибут, обращение к функции как к атрибуту вызывает ее метод __get__, чтобы вернуть что-то другое, чем сама функция - связанный метод. Когда у вас есть собственный класс без определения __get__, он просто возвращает ваш экземпляр без неявной передачи self.

Дескрипторы объясняются немного на http://docs.python.org/reference/datamodel.html#descriptors, если вы не знакомы с ними. Методы __get__, __set__ и __delete__ изменяют взаимодействие с вашим объектом как атрибутом.


Вы можете реализовать memoize как функцию и использовать встроенную магию __get__, которая уже есть у функций

import functools

def memoize(f):
    @functools.wraps(f)
    def memoized(*args, _cache={}): 
        # This abuses the normally-unwanted behaviour of mutable default arguments.
        if args not in _cache:
            _cache[args] = f(*args)
        return _cache[args]
    return memoized

или модифицировать ваш класс по примеру

import functools

class Memoize(object): #inherit object
    def __init__(self, function):
        self.function = function
        self.memoized = {}
    def __call__(self, *args): #don't accept kwargs you don't want.
        # I removed "hash = args" because it shadowed a builtin function and 
        # because it was untrue--it wasn't a hash, it was something you intended for
        # Python to hash for you.
        try:
            return self.memoized[args]
        except KeyError:
            self.memoized[args] = self.function(*args)
            return self.memoized[args]
    def __get__(self, obj, type):
        if obj is None: #We looked up on the class
            return self

        return functools.partial(self, obj)

Обратите внимание, что оба этих метода задыхаются, если любой из передаваемых вами аргументов является изменяемым (ну, технически нехешируемым). Это может подойти для вашего случая, но вы также можете захотеть разобраться со случаем, когда args является нехешируемым.

3
ответ дан 17 December 2019 в 18:13
поделиться
Другие вопросы по тегам:

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