Поскольку я работаю на Java, мне трудно понять, как объекты распределяются в Python. Рассмотрим этот скрипт Python:
x = ("a", "b")
y = ("a", "b")
print(x is y) # True
x = ["a", "b"]
y = ["a", "b"]
print(x is y) # False
В языках, подобных Java, ключевое слово new гарантирует, что будет создан новый экземпляр с другой ячейкой памяти. Но я предполагаю, что здесь дело не в этом. Так как же можно объяснить такое поведение? Есть ли в Python какой-либо пул неизменяемых типов для предотвращения дублирования литералов?
Я предполагаю, что Python отслеживает неизменяемые типы, поскольку один и тот же случай возвращает True для строк, комплексных чисел и других неизменяемых объектов. Но если так, то зачем беспокоиться?
Я не могу редактировать комментарий и сомневаюсь, что смогу выяснить его поведение, но если вы это сделаете x = ("a", "b"); y = ("a", "b")
, то x
и y
имеют одинаковую ссылку на память, т. е. x is y
имеет значение True. Пример списка тот же, всегда False. Случайное предположение: если оно интерпретируется в одном и том же «раунде» REPL, то ("a", "b")
рассматривается как то же самое неизменяемое значение и сохраняется как таковое. Таким образом, x и y указывают на один и тот же адрес памяти ?. Я думаю, люди, обладающие знаниями в реализации CPython, смогут вам ответить.
Я отредактировал свой пост, чтобы сделать часть кортежей более полной. Надеюсь, теперь мой ответ станет еще более полезным!
Не совсем верно, что в Java ключевое слово new гарантирует создание нового экземпляра с другой ячейкой памяти. Например, вы можете обновить две строки во время компиляции, и они будут «интернированы» - разделят уникальное место в памяти. Это похожая концепция — оптимизация для экономии памяти, где тип неизменяем. stackoverflow.com/questions/15805578/…
В вашем примере оба результата False
, я использую Python версии 3.11.0.
Ключевое слово is
используется для проверки того, ссылаются ли две переменные на один и тот же объект id() в памяти.
Используйте оператор ==
, чтобы проверить, равны ли значения двух переменных.
x = ("a", "b")
y = ("a", "b")
print(x is y) # False, the test is äquivalent to id(x) == id(y).
x = ["a", "b"]
y = ["a", "b"]
print(x is y) # False
y = x
print(x is y) # True
# because now
print("object id:", id(x), id(y)) # same objectID is referenced now.
Другой пример:
a=1
b=1
print(a == b)
print(a is b) # not valid for all int(), see e,f
print(id(a), id(b))
c = (1,)
d = (1,)
print(c == d)
print(c is d)
print(id(c), id(d))
e = 1098765432
f = 1098765432
print(e == f)
print(e is f)
print( id(e),id(f))
Выход:
True
True
4871700528 4871700528
True
False
4930976112 4930089312
True
False
4920911984 4920913552
PS: Очень хорошее объяснение можно найти здесь Лучано Рамальо | 15 октября 2014 г.
Кортеж() подобен константе, неизменяемой. Поэтому, когда вы говорите x = кортеж и говорите y = кортеж, где кортежи одинаковы, интерпретатор Python повторно использует одну и ту же память, а это означает, что первое значение x равно y истинно. Список [] является переменной, поэтому это дает нам ложь, поскольку они будут ссылаться на разные места в памяти. Каждая переменная является указателем на объект в Python. Вы можете увидеть это поведение, используя Print(öbject ...) после первой печати (x is y).
@DavidLipschitz Я согласен, что идентичные кортежи обычно хранятся в одном и том же месте памяти, потому что они неизменяемы. Списки действительно хранятся в разных ячейках памяти, поскольку они изменяемы. Итак, не могли бы вы проверить мой ответ? Вы тоже думаете, что код напечатает False
, как указано в комментарии Hermann12 к моему сообщению?
@Hermann12 Пожалуйста, пишите весь контент, включая вопросы, ответы, статьи, а также все комментарии на английском языке, потому что большинство из нас здесь не знают немецкого.
@LukeL, спасибо! Это была проблема с моим переводчиком Chrome. Исправлено, сейчас.
@ДэвидЛипшиц! Вы написали: «Кортеж() — это как бы константа, неизменяемая. Поэтому, когда вы говорите x = кортеж и говорите y = кортеж, где кортежи одинаковы, интерпретатор Python повторно использует одну и ту же память, а это означает, что первое значение x равно y истинно». - НО мой переводчик этого не сделал. Я проверял это несколько раз и не подтвердился. Если вы прочитаете другой комментарий, они согласятся, что интерпретатор выдает False. Я использую Python 3.11.0.
Для определения равенства двух объектов существует два метода:
is
определяет, имеют ли два объекта одинаковый идентификатор памяти. Другими словами, a is b
означает, что и a
, и b
хранятся в одной и той же ячейке памяти с одним и тем же id
. По сути, a is b
равно выражению id(a) == id(b)
.==
является оператором сравнения. Он проверяет, имеют ли два объекта одинаковое значение, но не обязательно хранятся ли они в одном и том же месте памяти.При создании объектов Python решает, будет ли новый объект использовать то же место в памяти (идентификатор), что и существующий объект. Например,
a = 1
b = 1
print(a == b) # always True
print(a is b) # most likely True
Результаты изложены в комментариях. a is b
в данном случае, вероятно, True
, а a == b
всегда True
. Но в этом коде
a = [{'a':1, 'b':2, 'c':3}, ([1, 2, 3], 7, 8, 9), 'foo', ('bar', 'eggs')]
b = [{'a':1, 'b':2, 'c':3}, ([1, 2, 3], 7, 8, 9), 'foo', ('bar', 'eggs')]
print(a == b) # always True
print(a is b) # most likely False
a is b
, вероятно, False
, а a == b
по-прежнему всегда True
. Эта разница связана с тем, как Python определяет, какое хранилище наиболее эффективно. В первом коде двум переменным a
и b
присвоено число 1
. Поскольку число 1
используется слишком часто и является неизменяемым объектом (помните, что int
— это неизменяемые объекты), Python, скорее всего, решит сохранить этот объект в том же месте памяти, что и другие 1
в этой или других программах. Однако,
[{'a':1, 'b':2, 'c':3}, ([1, 2, 3], 7, 8, 9), 'foo', ('bar', 'eggs')]
не слишком распространен, и объект является изменяемым (list
s изменяемы). Таким образом, в этом случае Python сохранит объект в другом месте памяти, поэтому is
может не вернуть True
, хотя оба объекта имеют одинаковое значение. Здесь чрезвычайно важно отметить, что все изменяемые объекты, созданные независимо, без какой-либо ссылки на другие объекты, всегда будут иметь другое место в памяти, независимо от фактического значения. Обратите внимание, что tuple
— это особый случай: они оба являются коллекциями, но также неизменяемы, как строки. Следующие три кода наглядно продемонстрируют эту концепцию:
# Program 1: Common and uncommon Immutable types
# Part I: Common
# define variables
a = 1
b = 1
c = 1
# check values
print(a == b) # True
print(a == c) # True
print(b == c) # True
# preform operation
print(a+b+c == a+b+c) # True
# use `is` to check values
print(a is b) # True
print(a is c) # True
print(b is c) # True
# You might expect `False` here, but keep in mind that `3` is another common integer which is immutable
print(a+b+c is a+b+c) # True
# Part II: Uncommon
# define variables
a2 = 123457997542
b2 = 123457997542
c2 = 123457997542
# check values
print(a2 == b2) # True
print(a2 == c2) # True
print(b2 == c2) # True
# preform operation
print(a2+b2+c2 == a2+b2+c2) # True
# use `is` to check values
print(a2 is b2) # True
print(a2 is c2) # True
print(b2 is c2) # True
print(a2+b2+c2 is a2+b2+c2) # False, because 740747985252 is not a common enough number that exists all the time
# Part III: tuples
print('entering tuples part')
t1 = (1, 2)
t2 = (1, 2)
t3 = (1, 2)
# check values
print(t1 == t2) # True
print(t1 == t3) # True
print(t2 == t3) # True
# preform operation
print(t1 + t2 + t3 == t1 + t2 + t3) # True
# Use `is`
print(t1 is t2) # True
print(t1 is t3) # True
print(t2 is t3) # True
# preform operation
print(t1 + t2 + t3 is t1 + t2 + t3) # False, as expected
# Program 2: Common and uncommon mutable objects
# Part I: common: lists
# define variables
a = [1, 2]
b = [1, 2]
c = [1, 2]
# check values
print(a == b) # True
print(a == c) # True
print(b == c) # True
# preform operation
print(a + b + c == a + b + c) # True
# Use `is`
print(a is b) # False
print(a is c) # False
print(b is c) # False
# preform operation
print(a + b + c is a + b + c) # False, as expected
# Part II: Uncommon
a2 = [{'a': 1, 'b': 2, 'c': 3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a': 1, 'b': 2}, (1, 2)), 'foo',
'bar'] # extremely weird and uncommon object; as expected, all `is` operation will be `False`
b2 = [{'a': 1, 'b': 2, 'c': 3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a': 1, 'b': 2}, (1, 2)), 'foo', 'bar']
c2 = [{'a': 1, 'b': 2, 'c': 3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a': 1, 'b': 2}, (1, 2)), 'foo', 'bar']
# check values
print(a2 == b2) # True
print(a2 == c2) # True
print(b2 == c2) # True
# preform operation
print(a2 + b2 + c2 == a2 + b2 + c2) # True
# Use `is`
print(a2 is b2) # False
print(a2 is c2) # False
print(b2 is c2) # False
# preform operation
print(a2 + b2 + c2 is a2 + b2 + c2) # False, as expected
# Program 3: Reference pointer; sometimes obscure and hard to understand
# Part I: Common immutable
# Part 1: common integers
# define variables
a = 1
b = a
c = b
# use `is` to check values
print(a is b) # True
print(a is c) # True
print(b is c) # True
print(a+b+c is a+b+c) # True
# Part 2: Uncommon integers
# define variables
a2 = 123457997542
b2 = a2
c2 = b2
# use `is` to check values
print(a2 is b2) # True
print(a2 is c2) # True
print(b2 is c2) # True
print(a2+b2+c2 is a2+b2+c2) # False, because 740747985252 is not a common enough number that exists all the time
del a, b, c, a2, b2, c2 # delete variables
# ===============================================================================================================
# Part II: Mutable and tuples
# Part 1: common: lists
# define variables
a = [1,2]
b = a
c = b
# Use `is`
print(a is b) # True
print(a is c) # True
print(b is c) # True
# preform operation
print(a+b+c is a+b+c) # False, as expected
del a, b, c
# Part 2: common: tuples
# define variables
print('entering tuples part')
a = (1, 2)
b = a
c = b
# Use `is`
print(a is b) # True
print(a is c) # True
print(b is c) # True
# preform operation
print(a+b+c is a+b+c) # False, as expected
# Note: the results of tuples are the same as with lists.
print('uncommon')
# Part III: Uncommon
a2 = [{'a':1, 'b':2, 'c':3}, ('1', '2', '3', '4'), [1, 2, 3, 4], ({'a':1, 'b':2}, (1, 2)), 'foo', 'bar'] # extremely weird and uncommon object; as expected, all `is` operation will be `False`
b2 = a2
c2 = b2
# Use `is`
print(a2 is b2) # True
print(a2 is c2) # True
print(b2 is c2) # True
# preform operation
print(a2+b2+c2 is a2+b2+c2) # False, as expected
# Notice all `is` results are `True` here no matter common or uncommon except for the last operation one
# ================================================================================================================
# Part III: List elements
# Part I: literal lists does not affect elements
l = [1, 2, 3]
l2 = l
l3 = [1, 2, 3]
print(l is l2) # True, same memory location
print(l[0] is l2[0]) # True, how the elements of a list or other collection are stored are not affected, same as in an independant variable
print(l[0] is l3[0]) # Still True
del l, l2
# Part II: elements cannot be changed by changing a copy of a element
l = [1, 2, 3]
l2 = l[0]
print(l, l2)
l2 = 1
print(l, l2)
del l, l2
# Part IV: Slice also cannot change the list
l = [1, 2, 3]
l2 = l[1:]
print(l, l2)
l2 = 4
print(l, l2)
del l, l2
# Part V: But, mutable objects within a list *can* be change by changing a copy of the object *IF* using append or other methods
l = [[1, 2, 3], [4, 5, 6]]
l2 = l[0]
l3 = l[0]
print(l, l2)
l2 = [7, 8, 9]
print(l, l2)
l3.append(10)
print(l, l3) # l changes
Хотя код немного длинный, я надеюсь, что вы сможете лучше понять модель памяти Python.
Примечание для комментариев к коду: «Часть», за которой следуют римские цифры, — это большие категории, а «Часть», за которой следуют арабские цифры, — это подкатегории ближайшей большой категории.
Обратите внимание, что во второй программе кортежи ведут себя иначе, чем списки. Проверка is
возвращает True
, поскольку в Python 3 кортежи с одинаковым значением обычно хранятся в одном и том же месте адреса памяти. В третьей программе результат такой же, как и в списках.
Чтобы получить количество ссылок определенного объекта, используйте sys.getrefcount()
:
>>> sys.getrefcount(1)
1796
>>> sys.getrefcount(9)
139
>>> sys.getrefcount([1,2])
1
>>> sys.getrefcount(2)
1131
>>> sys.getrefcount(200)
26
>>> sys.getrefcount(2002)
2
>>> sys.getrefcount([])
1
>>> sys.getrefcount({})
1
>>> sys.getrefcount(())
25760
>>> sys.getrefcount(True)
2356
>>> sys.getrefcount(False)
2943
>>> sys.getrefcount(list)
86
Обратите внимание, что даже пустые списки ([]
) и словари ({}
) не имеют на него никакой ссылки. Однако пустые кортежи (()
) имеют большое количество ссылок. Оба объекта True
и False
имеют большое количество ссылок.
ОП спросил не int(), а о кортежах(). Мы не знаем его версию Python, но в Python 3.11 два кортежа с одинаковым значением получают разные адреса памяти, в отличие от целых чисел. Ваше объяснение не полное.
@ Hermann12 Hermann12 Я согласен, что спрашивающий просил кортежи, но чтобы объяснить, как работает система памяти Python, необходимо также использовать целые числа, которые являются самым простым типом в качестве примера. Я забыл добавить часть кортежей, спасибо за напоминание. Я отредактирую свой пост, чтобы отразить это.
@ Hermann12 Hermann12 Привет, я только что отредактировал свой пост (это заняло некоторое время) и добавил часть кортежей. Надеюсь, это сделает мое решение более полным! Если вы считаете, что мой ответ стал лучше благодаря редактированию, проголосуйте за меня, чтобы мой ответ увидело больше людей. И еще раз спасибо за напоминание! Если бы не вы, я бы совсем забыл про кортежи.
В вашем коде я получаю разные результаты print(a2 is b2) # True print(a2 is c2) # True print(b2 is c2) # True - Последние 3 строки дают мне все значения False. Поэтому я не могу сказать, что ваше объяснение правильное или полное. Я использую Python 3.11.0. Кроме того, расширение кортежей дает мне значение False для всех трех строк: print(t1 is t2) # True print(t1 is t3) # True print(t2 is t3) # True.
@ Hermann12 Привет, я использую Python 3.10, и мои результаты верны в моей версии. Я не думаю, что между Python 3.10 и 3.11 есть большая разница, поэтому дважды проверьте свои результаты, чтобы убедиться, что они действительно печатаются False
.
@Hermann12 Однако я заметил, что результат действительно False
, когда я запускаю ``` >>> a = (1, 2) >>> b = (1, 2) >>> a == b True >>> a равно b False ``` в интерпретаторе. Однако результат такой же, как указано в комментарии, когда я запускаю файл Python, поэтому я не совсем уверен, что здесь происходит. Возможно, результаты просто различаются, но я уверен, что результаты действительно отображались True
, когда я запускал файл Python.
Я запускал его несколько раз, в результате так и не увидел True.
См. docs.python.org/3/reference/datamodel.html. Кроме того, как говорилось в других ответах, я не могу воспроизвести первую истину. Pyhon 3.10.14 может быть разницей в CPython или другой оптимизации, которая кэширует содержимое кортежа.