У меня есть украшенная функция (упрощенная версия):
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)
почему это не распространяет сам?
Проблема в том, что вы определили свой собственный вызываемый класс, а затем попытались использовать его как метод. Когда вы используете функцию как атрибут, обращение к функции как к атрибуту вызывает ее метод __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
является нехешируемым.