Почему setattr терпит неудачу на связанном методе - PullRequest
17 голосов
/ 25 октября 2011

В дальнейшем setattr завершается успешно при первом вызове, но завершается с ошибкой во втором:

AttributeError: 'method' object has no attribute 'i'

Почему это так, и есть ли способ установить атрибут для метода так, чтобы он существовал только в одном экземпляре, а не для каждого экземпляра класса?

class c:

    def m(self):

        print(type(c.m))
        setattr(c.m, 'i', 0)

        print(type(self.m))
        setattr(self.m, 'i', 0)

Python 3.2.2

Ответы [ 2 ]

30 голосов
/ 25 октября 2011

Краткий ответ: нет способа добавить пользовательские атрибуты в связанные методы.

Полный ответ следует.

В Python есть объекты функций и объекты методов . Когда вы определяете класс, оператор def создает функциональный объект , который живет в пространстве имен класса:

>>> class c:
...     def m(self):
...         pass
...
>>> c.m
<function m at 0x025FAE88>

Объекты функций имеют специальный атрибут __dict__, который может содержать определенные пользователем атрибуты:

>>> c.m.i = 0
>>> c.m.__dict__
{'i': 0}

Объектами метода являются разные звери. Это крошечные объекты, которые содержат ссылку на соответствующий объект функции (__func__) и ссылку на его хост-объект (__self__):

>>> c().m
<bound method c.m of <__main__.c object at 0x025206D0>>
>>> c().m.__self__
<__main__.c object at 0x02625070>
>>> c().m.__func__
<function m at 0x025FAE88>
>>> c().m.__func__ is c.m
True

Объекты метода предоставляют специальный __getattr__, который перенаправляет атрибут доступа к объекту функции:

>>> c().m.i
0

Это также верно для свойства __dict__:

>>> c().m.__dict__['a'] = 42
>>> c.m.a
42
>>> c().m.__dict__ is c.m.__dict__
True

При установке атрибутов следует правилам по умолчанию, и поскольку они не имеют своих собственных __dict__, невозможно установить произвольные атрибуты.

Это похоже на определяемые пользователем классы, определяющие __slots__ и не имеющие __dict__ слота, при попытке установить несуществующий слот поднимает AttributeError (см. документы по __slots__ для получения дополнительной информации):

>>> class c:
...     __slots__ = ('a', 'b')
...
>>> x = c()
>>> x.a = 1
>>> x.b = 2
>>> x.c = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'c' object has no attribute 'c'
2 голосов
/ 26 октября 2011

В: «Есть ли способ установить атрибут в методе так, чтобы он существовал только в одном экземпляре, а не для каждого экземпляра класса?»

A: Да:

class c:

    def m(self):

        print(type(c.m))
        setattr(c.m, 'i', 0)

        print(type(self))
        setattr(self, 'i', 0)

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

Но у методов есть экземпляр объекта, связанный с ними (self). Следовательно, вам не нужно устанавливать атрибуты для метода, так как вы можете просто установить его для экземпляра. Это именно то, для чего предназначен экземпляр.

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

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

...