Насколько уникален идентификатор Python ()? - PullRequest
0 голосов
/ 30 августа 2018

ТЛ; др Python повторно использует идентификаторы? Насколько вероятно, что два объекта с неперекрывающимся временем жизни получат одинаковый идентификатор?

Справочная информация: Я работал над сложным проектом, написанным исключительно на Python 3. Я видел некоторые проблемы в тестировании и потратил много времени на поиск первопричины. После некоторого анализа у меня возникло подозрение, что когда тестирование выполняется в целом (оно организовано и запускается специальным диспетчером), оно повторно использует некоторые смоделированные методы вместо того, чтобы устанавливать новые объекты их исходными методами. Чтобы проверить, использует ли переводчик повторно, я использовал id().

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

Возвращает «идентичность» объекта. Это целое число, которое гарантированно будет уникальным и постоянным для этого объекта в течение его жизни. Два объекта с неперекрывающимися временами жизни могут иметь одинаковое значение id().

Вопросы:

  1. Когда переводчик может повторно использовать id() значения? Это просто когда он случайно выбирает одну и ту же область памяти? Если это просто случайно, это кажется крайне маловероятным, но это все еще не гарантировано.

  2. Есть ли другой способ проверить, на какой объект я на самом деле ссылаюсь? Я столкнулся с ситуацией, когда у меня был объект, у него был издевательский метод. Объект больше не использовался, сборщик мусора уничтожил его. После этого я создаю новый объект того же класса, он получил новый id(), но метод получил тот же идентификатор, что и при имитации, и на самом деле был просто имитацией.

  3. Есть ли способ заставить Python уничтожить данный экземпляр объекта? Из прочитанного мною чтения выясняется, что нет, и что сборщик мусора не может найти ссылки на объект, но я подумал, что в любом случае стоит спросить.

Ответы [ 3 ]

0 голосов
/ 30 августа 2018
  1. Он может повторно использовать значение id, как только объект, который имел его, больше не находится ни в одной области видимости. Фактически, вероятно, его можно будет использовать повторно, если вы создадите похожий объект сразу после уничтожения первого.

  2. Если вы держите ссылку (в отличие от слабая ссылка ), id не используется повторно, поскольку объект все еще жив. Если вы просто держите значение id, вы, вероятно, делаете что-то не так.

  3. Нет, но вы можете удалить свою ссылку и запросить сборщик мусора для запуска . Сборщик мусора может не собрать этот объект, даже если в действительности нет живых ссылок.

0 голосов
/ 05 сентября 2018

Да, CPython повторно использует id() значения. Не рассчитывайте, что они уникальны в программе Python .

Это четко задокументировано :

Возвращает «идентичность» объекта. Это целое число, которое гарантированно будет уникальным и постоянным для этого объекта в течение срока его службы. Два объекта с неперекрывающимися временами жизни могут иметь одинаковое значение id ().

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

Обратите внимание, что это относится только к CPython, стандартной реализации, предоставляемой python.org. Существуют и другие реализации Python, такие как IronPython, Jython и PyPy, которые сами выбирают, как реализовать id(), потому что каждая из них может сделать свой выбор в отношении обработки времени жизни памяти и объектов.

Чтобы ответить на ваши конкретные вопросы:

  1. В CPython id() - это адрес памяти. Новые объекты будут размещены в следующем доступном пространстве памяти, поэтому, если на конкретном адресе памяти достаточно места для размещения следующего нового объекта, адрес памяти будет использован повторно. Это можно увидеть в интерпретаторе при создании новых объектов одинакового размера:

    >>> id(1234)
    4546982768
    >>> id(4321)
    4546982768
    

    Литерал 1234 создает новый целочисленный объект, для которого id() создает числовое значение. Поскольку больше нет ссылок на значение int, оно снова удаляется из памяти. Но снова выполняя то же выражение с другим целочисленным литералом, и, скорее всего, вы увидите то же значение id() (запуск сборщика мусора, прерывающий циклические ссылки, может освободить больше памяти, поэтому вы можете также не см. Тот же id() снова.

    Так что не случайно , но в CPython это функция алгоритмов выделения памяти.

  2. Если вам нужно проверить определенные объекты, сохраните собственную ссылку на него . Это может быть weakref слабая ссылка , если все, что вам нужно, это убедиться, что объект все еще "жив".

    Например, сначала запись ссылки на объект, а затем проверка:

    import weakref
    
    # record
    object_ref = weakref.ref(some_object)
    
    # check if it's the same object still
    some_other_reference is object_ref()   # only true if they are the same object
    

    Слабая ссылка не будет поддерживать объект в живых, но если он равен живым, то object_ref() вернет его (в противном случае вернет None).

    Вы можете использовать такой механизм для генерации действительно уникальных идентификаторов, см. Ниже.

  3. Все, что вам нужно сделать, чтобы «уничтожить» объект, это удалить все ссылки на него . Переменные (локальные и глобальные) являются ссылками. Как и атрибуты других объектов, а также записи в контейнерах, таких как списки, кортежи, словари, наборы и т. Д.

    В тот момент, когда все ссылки на объект исчезают, счетчик ссылок на объект падает до 0 и тут же удаляется.

    Сборка мусора необходима только для прерывания циклических ссылок , объектов, которые ссылаются только друг на друга, без дальнейших ссылок на цикл. Поскольку такой цикл никогда не достигнет счетчика ссылок 0 без помощи, сборщик мусора периодически проверяет такие циклы и прерывает одну из ссылок, чтобы помочь очистить эти объекты из памяти.

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

Если у вас должны быть уникальные идентификаторы для жизненного цикла Python-приложения , вам придется реализовать собственное средство. Если ваши объекты хешируемые и поддерживают слабые ссылки, то вы можете просто использовать WeakKeyDictionary экземпляр , чтобы связать произвольные объекты с UUIDs :

from weakref import WeakKeyDictionary
from collections import defaultdict
from uuid import uuid4

class UniqueIdMap(WeakKeyDictionary):
    def __init__(self, dict=None):
        super().__init__(self)
        # replace data with a defaultdict to generate uuids
        self.data = defaultdict(uuid4)
        if dict is not None:
            self.update(dict)

uniqueidmap = UniqueIdMap()

def uniqueid(obj):
    """Produce a unique integer id for the object.

    Object must me *hashable*. Id is a UUID and should be unique
    across Python invocations.

    """
    return uniqueidmap[obj].int

Это все еще производит целые числа, но поскольку они являются UUID, они не вполне гарантируют уникальность, но вероятность того, что вы когда-либо встретите тот же идентификатор во время вашего Время жизни меньше, чем от удара метеорита. См. Насколько уникален UUID?

Затем вы получаете уникальные идентификаторы даже для объектов с неперекрывающимися временами жизни:

>>> class Foo:
...     pass
...
>>> id(Foo())
4547149104
>>> id(Foo())  # memory address reused
4547149104
>>> uniqueid(Foo())
151797163173960170410969562162860139237
>>> uniqueid(Foo())  # but you still get a unique UUID
188632072566395632221804340107821543671
0 голосов
/ 30 августа 2018

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

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