В чем разница между "a is b" и "id (a) == id (b)" в Python? - PullRequest
33 голосов
/ 25 мая 2010

Встроенная функция 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 Ответ

56 голосов
/ 25 мая 2010
>>> 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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...