Подтвердить что ты не робот

Почему setattr терпит неудачу при связанном методе

В следующем случае 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

4b9b3361

Ответ 1

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

Далее следует длинный ответ.

В 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__ и no __dict__, при попытке установить несуществующий слот повышает значение AttributeError (см. docs на __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

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

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