Добавление атрибутов к методам экземпляра в Python

Я хотел бы добавить атрибут к методу экземпляра в одном из моих классов. Я пробовал ответить в на этот вопрос, но этот ответ работает только на функции - насколько я могу судить.

В качестве примера я хотел бы сделать что-то вроде:

class foo(object):
 ...
 def bar(self):
 self.bar.counter += 1
 return self.bar.counter
 bar.counter = 1
 ...

но, когда я вызываю foo(). bar() Я получаю:

AttributeError: 'instancemethod' object has no attribute 'counter'

Моя цель в том, чтобы попытаться произвести впечатление на то, что переменная 'counter' является локальной для метода bar(), а также во избежание загромождения пространства имен класса еще одним атрибутом. Есть ли способ сделать это? Есть ли более питонический способ сделать это?

4 ответа

В Python 3 ваш код будет работать, но в Python 2 есть некоторая упаковка, которая имеет место при поиске методов.

Класс vs Экземпляр

  • Уровень класса: сохранение counter с помощью функции (либо напрямую, либо с помощью изменчивого значения по умолчанию) эффективно делает его атрибутом уровня класса, так как существует только одна из функций, независимо от того, сколько экземпляров вы (все они имеют один и тот же функциональный объект).

  • Уровень экземпляра: чтобы сделать counter атрибут уровня экземпляра, вы должны создать функцию в __init__, а затем оберните ее с помощью functools.partial (чтобы она вела себя как обычный метод), а затем сохраните ее на экземпляре - теперь у вас есть один функциональный объект для каждого экземпляра.

Уровень класса

Принятой практикой для статической переменной является использование изменчивого аргумента по умолчанию:

class foo(object):
 ...
 def bar(self, _counter=[0]):
 _counter[0] += 1
 return _counter[0]

Если вы хотите, чтобы он был красивее, вы можете определить свой собственный изменяемый контейнер:

class MutableDefault(object):
 def __init__(self, start=0):
 self.value = start
 def __iadd__(self, other):
 self.value += other
 return self
 def value(self):
 return self.value

и измените свой код следующим образом:

class foo(object):
 def bar(self, _counter=MutableDefault()):
 _counter += 1
 return _counter.value

Уровень экземпляра

from functools import partial
class foo(object):
 def __init__(self):
 def bar(self, _counter=MutableDefault(1)): # create new 'bar' each time
 value = _counter.value
 _counter += 1
 return value
 self.bar = partial(bar, self)

Резюме

Как вы можете видеть, читаемость серьезно пострадала при переходе на уровень экземпляра для counter. Я настоятельно рекомендую вам переоценить важность подчеркивания того, что counter является частью bar, и если действительно важно, возможно, сделать bar своим собственным классом, экземпляры которого станут частью экземпляров foo. Если это не очень важно, делайте это обычным способом:

class foo(object):
 def __init__(self):
 self.bar_counter = 0
 def bar(self):
 self.bar_counter += 1
 return self.bar_counter


Извините, что выкопал более старую запись, но я наткнулся на нее в своих поисках и на самом деле нашел кого-то с решением.

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

http://metapython.blogspot.com/2010/11/python-instance-methods-how-are-they.html


Если вы хотите, чтобы счетчик был постоянным во всех экземплярах, вы можете сделать следующее: это все еще не Pythonic.

class foo(object):
 def bar(self):
 self.bar.im_func.counter += 1
 return self.bar.im_func.counter
 bar.counter = 0


Вам нужен метод __init__ для инициализации члена данных:

class foo(object):
 def __init__(self):
 self.counter = 0
 def bar(self):
 self.counter += 1
 return self.counter

Прикрепление значений данных непосредственно к функциям является явно непитоновым.

licensed under cc by-sa 3.0 with attribution.