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