добавить функцию украшения в класс - PullRequest
6 голосов
/ 23 марта 2010

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

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...

Теперь я хочу добавить этот метод в существующий класс.

ROOT.TChain.DrawPlot = _DrawPlot

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

chain = TChain()
chain.DrawPlot(opts)

Я получил:

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

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

1 Ответ

3 голосов
/ 23 марта 2010

Проблема в том, что вы определили свой собственный вызываемый класс, а затем попытались использовать его как метод. Когда вы используете функцию в качестве атрибута, доступ к функции в качестве атрибута вызывает ее метод __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 не подлежит отмене.

...