2016-04-20 5 views
4

В Python (2 и 3), мы можем присвоить атрибуты функции:Установка атрибутов метода после создания класса повышает «„instancemethod“объект не имеет атрибута», но attribiute явно есть

>>> 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 
+0

'self.foo'' '' '' '' '' '' '' 'функция, которую вы определили. Среди других отличительных особенностей 'self.foo' принимает нулевые аргументы, а функция' foo', которую вы написали, берет один. – user2357112

ответ

1

Установка foo.bar внутри тела класса работает, потому что foo фактическая foo функция. Тем не менее, когда вы

self.foo.bar = 456 

self.foo не то, что функция. self.foo - объект объекта объекта, созданный по требованию при его доступе. Вы не можете установить атрибуты на него по нескольким причинам:

  1. Если эти атрибуты сохраняются на функции foo, то присваивание a.foo.bar имеет неожиданный эффект на b.foo.bar, вопреки всем обычным ожиданиям о назначении атрибута.
  2. Если эти атрибуты хранятся на объекте метода экземпляра self.foo, они не будут отображаться при следующем доступе self.foo, потому что в следующий раз вы получите новый объект метода экземпляра.
  3. Если эти атрибуты хранятся на объекте экземпляра объекта self.foo, и вы меняете правила, так что self.foo всегда является одним и тем же объектом, а затем массивно раздувает каждый объект в Python, чтобы хранить кучу объектов объектов экземпляра, которые вам почти никогда не нужны.
  4. Если эти атрибуты хранятся в self.__dict__, что относительно объектов, которые не имеют __dict__? Кроме того, вам нужно будет придумать какое-то правило манипулирования именами или сохранить нестрочные ключи в self.__dict__, у которых есть свои проблемы.

Если вы хотите установить атрибуты на функцию foo после того, как определение класса будет сделано, вы можете сделать это с A.__dict__['foo'].bar = 456.(Я использовал A.__dict__, чтобы обойти вопрос о том, является ли функция A.foo функцией или объектом unbound метода, который зависит от вашей версии Python. Если A наследует foo, вам придется либо решить эту проблему, либо получить доступ к типу класс наследует foo от.)