id() против `is` оператора. Безопасно ли сравнивать идентификаторы? Означает ли один и тот же `id` один и тот же объект?

Насколько я могу положиться на объект id() а его уникальность на практике? Например:

  • Есть ли id(a) == id(b) имею в виду a is b или наоборот? А как же наоборот?
  • Насколько это безопасно, чтобы сохранить id где-то для последующего использования (например, в каком-то реестре вместо самого объекта)?

(Написано как предложенный канонический ответ на Canonicals for Python: объекты с одинаковым id() - один и тот же объект, оператор `is`, объекты несвязанного метода)

1 ответ

Решение

Согласно id() документация, id гарантированно будет уникальным

  1. на время жизни конкретного объекта, и
  2. в конкретном экземпляре интерпретатора

Таким образом, сравниваяids не является безопасным, если вы также не гарантируете, что оба объекта, чьи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
      

Из-за вышеуказанных требований безопасности при сравнении ids, сохраняя id Вместо этого объект не очень полезен, потому что вы все равно должны сохранить ссылку на сам объект - чтобы убедиться, что он остается живым. Также нет никакого увеличения производительности: is Реализация так же просто, как сравнение указателей.


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

Это не (технически) нарушает вышесказанное id() Уникальность документации обещает: повторно использованный объект остается живым на протяжении всего повторного использования.

Это также не имеет большого значения, потому что независимо от того, указывают ли две переменные на один и тот же объект или нет, практично знать, является ли объект изменчивым: если две переменные указывают на один и тот же изменяемый объект, изменение одной переменной (неожиданно) также изменит другую (неожиданно), У неизменяемых типов такой проблемы нет, поэтому для них не имеет значения, указывают ли две переменные на два одинаковых объекта или на один и тот же.


1Иногда это называется "неназванное выражение".

Другие вопросы по тегам