Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Оператор is и кортежи

#python #python_3x #операторы #tuple


Возник вопрос, почему происходит именно так:

>>> (1, 2) is (1, 2)
False
>>> () is ()
True


Ведь, кортеж - это неизменяемый тип данных, и два одинаковых кортежа должны быть
одним объектом в оперативной памяти.

Так например:

>>> 1 is 1
True
>>> 'str' is 'str'
True

    


Ответы

Ответ 1



Самое главное: Два одинаковых объекта не обязаны иметь одинаковый адрес в памяти, но это возможно. В Python 3.7, благодаря новой оптимизации AST, иногда так и происходит, в версиях ниже - нет. Полагаться на это поведение ни в коем случае нельзя. Теперь подробнее... Для начала стоит сказать, что это зависит от конкретной версии Python (точнее CPython). Python 3.7: print((1, 2) is (1, 2)) # True Python 3.6: print((1, 2) is (1, 2)) # False Для того, чтобы понять некоторые различия в версиях, предлагаю дизассемблировать этот код. Начнём со старой версии (3.6): print(dis.dis('(1, 2) is (1, 2)')) 1 0 LOAD_CONST 2 ((1, 2)) 2 LOAD_CONST 3 ((1, 2)) 4 COMPARE_OP 8 (is) 6 RETURN_VALUE 4-ая колонка с числами (2, 3, 8) - это opargs (аргументы операции). Обратите внимание на то, что команда одна и та же (LOAD_CONST), данные одни и те же ((1, 2)), но эти данные являются разными аргументами при операции. Следовательно, они будут разными объектами в памяти (но я немного сомневаюсь насчёт этого утверждения) Давайте взглянем на список констант скомпилированного кода, чтобы точно в этом убедиться: code = compile('(1, 2) is (1, 2)', '', 'single') print(code.co_consts) (1, 2, None, (1, 2), (1, 2)) Кортеж (1, 2) встречается 2 раза, то есть это разные объекты. Новая версия (3.7): print(dis.dis('(1, 2) is (1, 2)')) 1 0 LOAD_CONST 0 ((1, 2)) 2 LOAD_CONST 0 ((1, 2)) 4 COMPARE_OP 8 (is) 6 RETURN_VALUE Уже на этом этапе видно, что наш кортеж является одним и тем же аргументом в операции. Подтвердим наши догадки, взглянув на константы: code = compile('(1, 2) is (1, 2)', '', 'single') print(code.co_consts) ((1, 2), None) Как видим, объект один, поэтому и адрес в памяти один. Результат в новой версии отличается из-за перемещения некоторых оптимизаций байт-кода в новый оптимизатор AST. Выдержка из нововведений: Constant folding has been moved from the peephole optimizer to the new AST optimizer, which is able perform optimizations more consistently. (Contributed by Eugene Toder and INADA Naoki in bpo-29469 and bpo-11549.) Поэтому скомпилированный код очень сильно отличается от интерактивного сеанса: Интерактивный режим: >>> x = (1, 2) >>> y = (1, 2) >>> print(x is y) False >>> print((3, 4) is (3, 4)) True Скомпилированная версия: x = (1, 2) y = (1, 2) print(x is y) # True print((3, 4) is (3, 4)) # True Подобные оптимизации касаются также других объектов. Также в разных версиях отличается результат следующих инструкций: x, y = 257, 257 x is y -6 is -6 Давние особенности Тестирую Python 3.7 в интерактивном режиме. Сначала немного примеров: №1: >>> x = 257 >>> y = 257 >>> x is y False №2: >>> x = 256 >>> y = 256 >>> x is y True №3: >>> x = -6 >>> y = -6 >>> x is y False №4: >>> x = -5 >>> y = -5 >>> x is y True №5 >>> x = 'Loremipsumdolorsitametconsecteturadipiscingelit' >>> y = 'Loremipsumdolorsitametconsecteturadipiscingelit' >>> x is y True №6 >>> x = 'Hi!' >>> y = 'Hi!' >>> x is y False Из этих примеров видно, что Python кэширует числа в промежутке [-5; 256]. Пруф Также он кэширует простые строки. Пруф Но если запускать скомпилированный код, то в Python 3.7 все примеры вернут True. На основании примеров выше, хочу предупредить, что сравнивать любые данные оператором is ни в коем случае нельзя. Используйте для этого только оператор ==.

Ответ 2



Оператор is используется для проверки того, указывают ли сравниваемые переменные на один и тот же объект в памяти. Пустые кортежи, вероятно, указывают на NULL или же нечто подобное, потому идентичны. Два заполненных кортежа – самостоятельные объекты в памяти. Это то же самое, что объявить две переменных в C++ и сравнить их адреса. Очевидно, что результат будет разным. Вы говорили: кортеж - это неизменяемый тип данных и два одинаковых кортежа должны быть одним объектом в оперативной памяти однако в этом высказывании первое утверждение отнюдь не влечёт второе. Вернитесь к аналогии с переменными C++, приведённой выше, если причина всё ещё не понятна.

Комментариев нет:

Отправить комментарий