2012-06-16 2 views
4

Я считаю, следующий пример слегка удивительно:Python правило подстановки метод

>>> class Foo: 
     def blah(self): 
      pass 


>>> f = Foo() 
>>> def bar(self): 
     pass 

>>> Foo.bar = bar 
>>> f.bar 
<bound method Foo.bar of <__main__.Foo object at 0x02D18FB0>> 

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

>>> g = Foo() 
>>> g.blah is f.blah 
False 

Но мое понимание процесса, несомненно, недостатки , так как я не ожидал, что назначение функции в атрибут класса приведет к тому, что уже был создан к.

Итак, мой вопрос заключается в два раза -

  1. Почему назначение функции в классе применяются задним числом экземпляров? Каковы фактические правила и процессы поиска, которые делают это так?
  2. Является ли это чем-то гарантированным языком или просто случаем?

ответ

11

Вы хотите, чтобы взорвать ваш ум, попробуйте следующее:

f.blah is f.blah 

Это верно, метод экземпляра обертка отличается каждый раз, когда вы к нему доступ.

Фактически метод экземпляра является дескриптором. Другими словами, на самом деле f.blah:

Foo.blah.__get__(f, type(f)) 

методы фактически не хранятся на экземпляре; они хранятся в классе, и оболочка метода генерируется «на лету», чтобы привязать метод к экземпляру.

+0

А, справа.Я не понял/забыл, что протокол дескриптора передал экземпляр в '__get__'. Это научит меня читать * все * соответствующие документы. :-) – lvc

+0

cf http://wiki.python.org/moin/FromFunctionToMethod для получения дополнительной информации об этом. –

7

Экземпляры не «содержат» метод. Процесс поиска происходит динамически во время доступа к foo.bar. Он проверяет, имеет ли экземпляр атрибут этого имени. Так как это не так, он смотрит на класс, после чего он находит любой атрибут, который имеет класс в то время. Обратите внимание, что методы не являются особыми в этом отношении. Вы увидите тот же эффект, если вы установили Foo.bar = 2; после этого foo.bar будет оцениваться до 2.

Что гарантируется языком, так это то, что поиск атрибутов происходит следующим образом: сначала экземпляр, затем класс, если атрибут не найден в экземпляре. (Правила поиска различны для special methods implicitly invoked via operator overloading, etc..)

Только если вы напрямую назначаете атрибут экземпляру, он будет маскировать атрибут класса.

>>> foo = Foo() 
>>> foo.bar 
Traceback (most recent call last): 
    File "<pyshell#79>", line 1, in <module> 
    foo.bar 
AttributeError: 'Foo' object has no attribute 'bar' 
>>> foo.bar = 2 
>>> Foo.bar = 88 
>>> foo.bar 
2 

Все вышеперечисленное является отдельным вопросом из связанных/несвязанных методов. Механизм класса в Python использует descriptor protocol, так что, когда вы получаете доступ к foo.bar, новый экземпляр связанного метода создается «на лету». Вот почему вы видите разные экземпляры связанных меток на разных объектах. Но обратите внимание, что эти связанные методы основаны на одном и том же кодовом объекте, как определено методом, который вы написали в классе:

>>> foo = Foo() 
>>> foo2 = Foo() 
>>> foo.blah.im_func.__code__ is foo2.blah.im_func.__code__ 
True