Установка атрибутов в методе после создания класса вызывает "объект instancemethod не имеет атрибута", но атрибут явно присутствует
В Python (2 и 3) мы можем назначить атрибуты функции:
>>> class A(object):
... def foo(self):
... """ This is obviously just an example """
... return "FOO{}!!".format(self.foo.bar)
... foo.bar = 123
...
>>> a = A()
>>> a.foo()
'FOO123!!'
И это круто.
Но почему мы не можем изменить foo.bar
позже? Например, в конструкторе, вот так:
>>> class A(object):
... def __init__(self, *args, **kwargs):
... super(A, self).__init__(*args, **kwargs)
... print(self.foo.bar)
... self.foo.bar = 456 # KABOOM!
... def foo(self):
... """ This is obviously just an example """
... return "FOO{}!!".format(self.foo.bar)
... foo.bar = 123
...
>>> a = A()
123
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __init__
AttributeError: 'instancemethod' object has no attribute 'bar'
Python претензий нет bar
хотя он напечатал это хорошо только на предыдущей строке.
Та же самая ошибка происходит, если мы пытаемся изменить это непосредственно на классе:
>>> A.foo.bar
123
>>> A.foo.bar = 345 # KABOOM!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'instancemethod' object has no attribute 'bar'
Что здесь происходит, то есть почему мы видим это поведение?
Есть ли способ установить атрибуты функции после создания класса?
(Мне известно о нескольких альтернативах, но я явно задаюсь вопросом об атрибутах методов здесь или, возможно, более широкой проблеме.)
Мотивация: Django использует возможность устанавливать атрибуты на методы, например:
class MyModelAdmin(ModelAdmin):
...
def custom_admin_column(self, obj):
return obj.something()
custom_admin_column.admin_order_field ='relation__field__span'
custom_admin_column.allow_tags = True
1 ответ
Настройка foo.bar
внутри тела класса работает, потому что foo
является фактическим foo
функция. Тем не менее, когда вы делаете
self.foo.bar = 456
self.foo
это не та функция. self.foo
является объектом метода экземпляра, созданным по требованию при обращении к нему. Вы не можете установить атрибуты для этого по нескольким причинам:
- Если эти атрибуты хранятся в
foo
функции, а затем присваиваяa.foo.bar
оказывает неожиданное влияние наb.foo.bar
вопреки всем обычным ожиданиям по поводу присвоения атрибутов. - Если эти атрибуты хранятся в
self.foo
объект экземпляра метода, они не будут отображаться при следующем обращенииself.foo
потому что вы получите новый объект метода экземпляра в следующий раз. - Если эти атрибуты хранятся в
self.foo
объект экземпляра метода, и вы измените правила такself.foo
это всегда один и тот же объект, то это массово раздувает каждый объект в Python, чтобы хранить кучу объектов метода экземпляра, которые вам почти никогда не нужны. - Если эти атрибуты хранятся в
self.__dict__
что насчет объектов, которые не имеют__dict__
? Кроме того, вам нужно придумать какое-то правило искажения имен или хранить нестроковые ключи вself.__dict__
У обоих есть свои проблемы.
Если вы хотите установить атрибуты на foo
Функция после определения класса, вы можете сделать это с A.__dict__['foo'].bar = 456
, (Я использовал A.__dict__
обойти вопрос о том, A.foo
это функция или объект несвязанного метода, который зависит от вашей версии Python. Если A
наследуется foo
вам придется либо разобраться с этой проблемой, либо получить доступ к классу наследуемого им класса foo
от.)