В чем разница между "a is b" и "id (a) == id (b)" в Python?

id() встроенная функция дает...

целое число (или длинное целое число), которое гарантировано будет уникальным и постоянным для этого объекта в течение его жизни.

Вместо этого оператор is дает...

идентификатор объекта

Итак, почему возможно иметь два объекта с тем же id, но вернуть False в проверку is? Вот пример:

>>> class Test():
...   def test():
...     pass
>>> a = Test()
>>> b = Test()
>>> id(a.test) == id(b.test)
True
>>> a.test is b.test
False

Более тревожный пример: (продолжение выше)

>>> b = a
>>> b is a
True
>>> b.test is a.test
False
>>> a.test is a.test
False

Однако:

>>> new_improved_test_method = lambda: None
>>> a.test = new_improved_test_method
>>> a.test is a.test
True

Ответ 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

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