Я думал, что оператор 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
Я думал, что оператор 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
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)
Одинаковое расположение памяти используется 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)
создается новый объект метода.
a.f
, его копия создается в памяти. Эта ячейка памяти возвращается id
a.g
его копия создается в том же адресе памяти, который вы снова получаете с помощью id
Хороший недостаток!
a.f и a.g - разные объекты.
оператор возвращает true только тогда, когда они являются одним объектом.
но два объекта с неперекрывающимися временами жизни могут иметь одинаковое значение id().
обратитесь здесь для оператора id
Оператор 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