Ответ 1
>>> b.test is a.test
False
>>> a.test is a.test
False
Методы создаются "на лету" каждый раз, когда вы просматриваете их. Объект функции (который всегда является одним и тем же объектом) реализует протокол дескриптора, а его __get__
создает объект связанного метода. Нет двух связанных методов, как правило, одного и того же объекта.
>>> id(a.test) == id(b.test)
True
>>> a.test is b.test
False
Этот пример обманчив. Результатом первого является только True
по совпадению. a.test
создает связанный метод и мусор, собранный после вычисления id(a.test)
, потому что ссылки на него отсутствуют. (Обратите внимание, что вы цитируете документацию о том, что идентификатор является "уникальным и постоянным для этого объекта в течение его жизненного цикла" (выделение мое).) b.test
имеет тот же идентификатор, что и связанный метод, который у вас был до этого, и это разрешалось, потому что никакие другие объекты не имеют того же идентификатора.
Обратите внимание, что вы редко используете is
и даже реже используете id
. id(foo) == id(bar)
всегда неверно.
Что касается вашего нового примера, надеюсь, вы получите то, что он делает сейчас:
>>> new_improved_test_method = lambda: None
>>> a.test = new_improved_test_method
>>> a.test is a.test
True
В этом случае мы не делаем методы "на лету" из функций класса, автоматически связывающих себя и возвращающие объекты связанных объектов. В этом случае вы просто сохраняете функцию как атрибут экземпляра. Ничего особенного не происходит при поиске (дескрипторы вызываются только при поиске атрибута класса), поэтому каждый раз, когда вы просматриваете этот атрибут, вы получаете исходный объект, который вы сохранили.