Добавление атрибутов к методам экземпляра в Python - PullRequest
9 голосов
/ 02 марта 2012

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

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

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 (), а также чтобы не загромождать пространство имен моего класса еще одним атрибутом. Есть ли способ сделать это? Есть ли более питонический способ сделать это?

Ответы [ 5 ]

8 голосов
/ 02 марта 2012

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

Класс против экземпляра

  • уровень класса: сохранение 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
2 голосов
/ 26 декабря 2012

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

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

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

0 голосов
/ 14 мая 2019

Вы не можете добавить атрибут непосредственно к методу instance, но метод instance является оберткой для функции, и вы можете добавить атрибут в упакованную функцию.то есть

class foo(object):
    def bar(self):
        # When invoked bar is an instancemethod.
        self.bar.__func__.counter = self.bar.__func__.counter + 1
        return self.bar.__func__.counter
    bar.counter = 1 # At this point bar is just a function

Это работает, но счетчик фактически становится переменной класса.

0 голосов
/ 02 марта 2012

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

class foo(object):
    def bar(self):
        self.bar.im_func.counter += 1
        return self.bar.im_func.counter
    bar.counter = 0
0 голосов
/ 02 марта 2012

Вам необходим метод __init__ для инициализации элемента данных:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...