Поведение создания объекта Python

Поскольку я работаю на 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 для строк, комплексных чисел и других неизменяемых объектов. Но если так, то зачем беспокоиться?

См. docs.python.org/3/reference/datamodel.html. Кроме того, как говорилось в других ответах, я не могу воспроизвести первую истину. Pyhon 3.10.14 может быть разницей в CPython или другой оптимизации, которая кэширует содержимое кортежа.

MufasaChan 13.07.2024 23:32

Я не могу редактировать комментарий и сомневаюсь, что смогу выяснить его поведение, но если вы это сделаете x = ("a", "b"); y = ("a", "b"), то x и y имеют одинаковую ссылку на память, т. е. x is y имеет значение True. Пример списка тот же, всегда False. Случайное предположение: если оно интерпретируется в одном и том же «раунде» REPL, то ("a", "b") рассматривается как то же самое неизменяемое значение и сохраняется как таковое. Таким образом, x и y указывают на один и тот же адрес памяти ?. Я думаю, люди, обладающие знаниями в реализации CPython, смогут вам ответить.

MufasaChan 14.07.2024 00:50

Я отредактировал свой пост, чтобы сделать часть кортежей более полной. Надеюсь, теперь мой ответ станет еще более полезным!

Luke L 19.07.2024 08:48

Не совсем верно, что в Java ключевое слово new гарантирует создание нового экземпляра с другой ячейкой памяти. Например, вы можете обновить две строки во время компиляции, и они будут «интернированы» - разделят уникальное место в памяти. Это похожая концепция — оптимизация для экономии памяти, где тип неизменяем. stackoverflow.com/questions/15805578/…

topsail 19.07.2024 15:52
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
5
4
163
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В вашем примере оба результата 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).

David Lipschitz 17.07.2024 20:42

@DavidLipschitz Я согласен, что идентичные кортежи обычно хранятся в одном и том же месте памяти, потому что они неизменяемы. Списки действительно хранятся в разных ячейках памяти, поскольку они изменяемы. Итак, не могли бы вы проверить мой ответ? Вы тоже думаете, что код напечатает False, как указано в комментарии Hermann12 к моему сообщению?

Luke L 19.07.2024 21:17

@Hermann12 Пожалуйста, пишите весь контент, включая вопросы, ответы, статьи, а также все комментарии на английском языке, потому что большинство из нас здесь не знают немецкого.

Luke L 19.07.2024 21:25

@LukeL, спасибо! Это была проблема с моим переводчиком Chrome. Исправлено, сейчас.

Hermann12 20.07.2024 06:21

@ДэвидЛипшиц! Вы написали: «Кортеж() — это как бы константа, неизменяемая. Поэтому, когда вы говорите x = кортеж и говорите y = кортеж, где кортежи одинаковы, интерпретатор Python повторно использует одну и ту же память, а это означает, что первое значение x равно y истинно». - НО мой переводчик этого не сделал. Я проверял это несколько раз и не подтвердился. Если вы прочитаете другой комментарий, они согласятся, что интерпретатор выдает False. Я использую Python 3.11.0.

Hermann12 20.07.2024 06:22
Ответ принят как подходящий

Для определения равенства двух объектов существует два метода:

  1. Ключевое слово is определяет, имеют ли два объекта одинаковый идентификатор памяти. Другими словами, a is b означает, что и a, и b хранятся в одной и той же ячейке памяти с одним и тем же id. По сути, a is b равно выражению id(a) == id(b).
  2. Часть выражения == является оператором сравнения. Он проверяет, имеют ли два объекта одинаковое значение, но не обязательно хранятся ли они в одном и том же месте памяти.

При создании объектов 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')]

не слишком распространен, и объект является изменяемым (lists изменяемы). Таким образом, в этом случае 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 18.07.2024 10:01

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

Luke L 18.07.2024 21:18

@ Hermann12 Hermann12 Привет, я только что отредактировал свой пост (это заняло некоторое время) и добавил часть кортежей. Надеюсь, это сделает мое решение более полным! Если вы считаете, что мой ответ стал лучше благодаря редактированию, проголосуйте за меня, чтобы мой ответ увидело больше людей. И еще раз спасибо за напоминание! Если бы не вы, я бы совсем забыл про кортежи.

Luke L 19.07.2024 08:42

В вашем коде я получаю разные результаты 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 19.07.2024 11:01

@ Hermann12 Привет, я использую Python 3.10, и мои результаты верны в моей версии. Я не думаю, что между Python 3.10 и 3.11 есть большая разница, поэтому дважды проверьте свои результаты, чтобы убедиться, что они действительно печатаются False.

Luke L 19.07.2024 21:02

@Hermann12 Однако я заметил, что результат действительно False, когда я запускаю ``` >>> a = (1, 2) >>> b = (1, 2) >>> a == b True >>> a равно b False ``` в интерпретаторе. Однако результат такой же, как указано в комментарии, когда я запускаю файл Python, поэтому я не совсем уверен, что здесь происходит. Возможно, результаты просто различаются, но я уверен, что результаты действительно отображались True, когда я запускал файл Python.

Luke L 19.07.2024 21:08

Я запускал его несколько раз, в результате так и не увидел True.

Hermann12 20.07.2024 06:34

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