id() против `is` оператора. Безопасно ли сравнивать идентификаторы? Означает ли один и тот же `id` один и тот же объект?
Насколько я могу положиться на объект id()
а его уникальность на практике? Например:
- Есть ли
id(a) == id(b)
имею в видуa is b
или наоборот? А как же наоборот? - Насколько это безопасно, чтобы сохранить
id
где-то для последующего использования (например, в каком-то реестре вместо самого объекта)?
(Написано как предложенный канонический ответ на Canonicals for Python: объекты с одинаковым id() - один и тот же объект, оператор `is`, объекты несвязанного метода)
1 ответ
Согласно id()
документация, id
гарантированно будет уникальным
- на время жизни конкретного объекта, и
- в конкретном экземпляре интерпретатора
Таким образом, сравниваяid
s не является безопасным, если вы также не гарантируете, что оба объекта, чьиid
Взятые еще живы во время сравнения(и связаны с тем же экземпляром интерпретатора Python, но вам нужно действительно попытаться сделать так, чтобы это стало ложным).
Что именноis
делает - что делает сравнениеid
с избыточностью. Если вы не можете использоватьis
Синтаксис по любой причине, всегда естьoperator.is_
,
Теперь, является ли объект еще живым во время сравнения, не всегда очевидно(и иногда совершенно неочевидно):
Доступ к некоторым атрибутам (например, связанные методы объекта) каждый раз создает новый объект. Итак, результат
id
может или не может быть одинаковым на каждом атрибуте доступа.Пример:
>>> class C(object): pass >>> c=C() >>> c.a=1 >>> c.a is c.a True # same object each time >>> c.__init__ is c.__init__ False # a different object each time # The above two are not the only possible cases. # An attribute may be implemented to sometimes return the same object # and sometimes a different one: @property def page(self): if check_for_new_version(): self._page=get_new_version() return self._page
Если объект создан в результате вычисления выражения и нигде не сохранен, он немедленно удаляется1, и любой объект, созданный после этого, может занять его
id
,Это даже верно в пределах одной строки кода. Например, результат
id(create_foo()) == id(create_bar())
не определеноПример:
>>> id([]) #the list object is discarded when id() returns 39733320L >>> id([]) #a new, unrelated object is created (and discarded, too) 39733320L #its id can happen to be the same >>> id([[]]) 39733640L #or not >>> id([]) 39733640L #you never really know
Из-за вышеуказанных требований безопасности при сравнении id
s, сохраняя id
Вместо этого объект не очень полезен, потому что вы все равно должны сохранить ссылку на сам объект - чтобы убедиться, что он остается живым. Также нет никакого увеличения производительности: is
Реализация так же просто, как сравнение указателей.
Наконец, в качестве внутренней оптимизации (и деталей реализации, так что она может отличаться в разных реализациях и выпусках), CPython повторно использует некоторые часто используемые простые объекты неизменяемых типов. На момент написания статьи это включает в себя маленькие целые числа и некоторые строки. Так что даже если вы получили их из разных мест, их id
s может совпадать.
Это не (технически) нарушает вышесказанное id()
Уникальность документации обещает: повторно использованный объект остается живым на протяжении всего повторного использования.
Это также не имеет большого значения, потому что независимо от того, указывают ли две переменные на один и тот же объект или нет, практично знать, является ли объект изменчивым: если две переменные указывают на один и тот же изменяемый объект, изменение одной переменной (неожиданно) также изменит другую (неожиданно), У неизменяемых типов такой проблемы нет, поэтому для них не имеет значения, указывают ли две переменные на два одинаковых объекта или на один и тот же.
1Иногда это называется "неназванное выражение".