Подтвердить что ты не робот

Почему разные методы одного и того же объекта имеют один и тот же идентификатор?

Я думал, что оператор is проверяет равенство объектов id. Но это не выглядит так.

>>> class A(object):
...   def f(): return 1
...   def g(): return 2
... 
>>> a = A()
>>> a.f is a.g
False
>>> id(a.f) == id(a.g)
True
4b9b3361

Ответ 1

Python повторно использует ту же ячейку памяти, что и у вас нет других ссылок на объекты, после того как оценивается id(a.f), есть больше ссылок на объект, так что это gc'd, тогда python может повторно использовать одно и то же место памяти для a.g. если вы назначаете методы именам, вы увидите другое поведение:

# creates a reference to the method f
In [190]: f = a.f
# creates a reference to the method g
In [191]: g = a.g
# cannot reuse the memory location of f as it is still referenced
In [192]: id(f) == id(g)
Out[192]: False

На самом деле вам действительно нужно хранить ссылку на f, чтобы увидеть то же поведение, что и выше.

In [201]: f = a.f

In [202]: id(f) == id(a.g)
Out[202]: False

Вы можете увидеть количество ссылок с sys.getrefcount или gc.gc.get_referrers:

In [2]: import gc

In [3]: f = a.f

In [4]: len(gc.get_referrers(a.g)),len(gc.get_referrers(f))
Out[4]: (0, 1)

In [5]: sys.getrefcount(a.g),sys.getrefcount(f)
Out[5]: (1, 2)

Единственная причина, по которой вы видите 1 для a.g, заключается в том, что возвращенный счет обычно выше, чем вы могли ожидать, потому что он включает (временную) ссылку в качестве аргумента для getrefcount(). Это аналогично вашему собственному примеру, после вычисления метода вы все равно будете иметь ссылку на f, при этом a.g refcount будет равен 0, поэтому он сразу же собирает мусор, а python может использовать память для ничего другого.

Также стоит отметить, что поведение не ограничивается методами, но это всего лишь деталь реализации cpython, а не то, на что вы должны полагаться:

In [67]: id([]), id([])
Out[67]: (139746946179848, 139746946179848)

In [73]: id(tuple()),id([]),id([])
Out[73]: (139747414818888, 139746946217544, 139746946217544)

In [74]: id([]),id([]),id([])
Out[74]: (139746946182024, 139746946182024, 139746946182024)

In [75]: id([]),id(tuple()),id([])
Out[75]: (139746946186888, 139747414818888, 139746946186888)

In [76]: id(tuple()),id([]),id(tuple())
Out[76]: (139747414818888, 139746946217736, 139747414818888)

Ответ 2

Одинаковое расположение памяти используется python для методов a.f и a.g, которые представляют собой ** два объекта с неперекрывающимися сроками жизни *, поэтому id возвращает одинаковое имя для обоих из них, см. более подробные объяснения ниже.

Из документации для находится оператор:

Операторы являются и не проверяются для идентичности объекта: x is y истинно тогда и только тогда, когда x и y являются одним и тем же объектом.

Из документации для id

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

Разъяснения: Всякий раз, когда вы просматриваете метод с помощью class.name или instance.name, объект метода создается a-new. Python использует протокол дескриптора, чтобы каждый раз обматывать функцию в объекте метода.

Итак, при поиске id(a.f) или id(a.g) создается новый объект метода.

  • Когда вы обманываете id a.f, его копия создается в памяти. Эта ячейка памяти возвращается id
  • Поскольку нет ссылок на вновь созданный метод, он исправляется GC (теперь адрес памяти снова доступен)
  • После получения идентификатора a.g его копия создается в том же адресе памяти, который вы снова получаете с помощью id
  • у вас есть сравнение правдивое id

Хороший недостаток!

Ответ 3

a.f и a.g - разные объекты.
оператор возвращает true только тогда, когда они являются одним объектом. но два объекта с неперекрывающимися временами жизни могут иметь одинаковое значение id().

обратитесь здесь для оператора id

Ответ 4

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

И о следующей части:

>>> id(a.f) == id(a.g)
True

Так как python создает объекты во время выполнения, первый раз, когда python пытается получить идентификатор a.f, a.g не определен и основан на викторине python Два объекта с неперекрывающимися времена жизни могут иметь одинаковое значение id(). SO в этом случае объекты a.f и a.g, которые имеют неперекрывающиеся времена жизни, имеют равный id.

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

Несколько дополнительных заметок о is:

Как я уже говорил в вышеупомянутых строках, оператор будет проверять идентичность объектов, а объект в python будет создан во время выполнения. Но это не верно для некоторых небольших типов, таких как целые числа и строки, потому что ваши - это однотонные объекты, а не объекты python. Следовательно, он будет сразу расположен в памяти, например, типа C.

Для лучшей демонстрации вы можете увидеть следующие примеры:

>>> 100 is 10*10
True
>>> 
>>> 1000 is 10*100
False
>>> 

И для строк:

>>> 'aaaa'*5 is 'a'*20
True
>>> 'aaaa'*50 is 'a'*200
False