Ответ 1
Это поведение, по крайней мере, частично связано с тем, как интерпретатор выполняет постоянную фальцовку и как REPL выполняет код.
Во-первых, помните, что CPython сначала компилирует код (в AST, а затем в байт-код). Затем он оценивает байткод. Во время компиляции script ищет объекты, которые неизменяемы и кэшируют их. Это также дедуплицирует их. Поэтому, если он видит
a = 257
b = 257
он сохранит a и b в отношении одного и того же объекта:
import dis
def f():
a = 257
b = 257
dis.dis(f)
#>>> 4 0 LOAD_CONST 1 (257)
#>>> 3 STORE_FAST 0 (a)
#>>>
#>>> 5 6 LOAD_CONST 1 (257)
#>>> 9 STORE_FAST 1 (b)
#>>> 12 LOAD_CONST 0 (None)
#>>> 15 RETURN_VALUE
Обратите внимание на LOAD_CONST 1
. 1
- это индекс в co_consts
:
f.__code__.co_consts
#>>> (None, 257)
Таким образом, оба загружают одинаковые 257
. Почему это не происходит:
$ python2
Python 2.7.8 (default, Sep 24 2014, 18:26:21)
>>> a = 257
>>> b = 257
>>> a is b
False
$ python3
Python 3.4.2 (default, Oct 8 2014, 13:44:52)
>>> a = 257
>>> b = 257
>>> a is b
False
?
Каждая строка в этом случае является отдельной единицей компиляции, а дедупликация не может произойти через них. Он работает аналогично
compile a = 257
run a = 257
compile b = 257
run b = 257
compile a is b
run a is b
Таким образом, эти объекты кода будут иметь уникальные постоянные кеши.
Это означает, что если мы удалим разрыв строки, is
вернет True
:
>>> a = 257; b = 257
>>> a is b
True
Действительно, это верно для обеих версий Python. На самом деле, именно поэтому
>>> a, b = 257, 257
>>> a is b
True
возвращает True
; это не из-за какого-либо атрибута распаковки; Oни
просто поместите его в ту же единицу компиляции.
Это возвращает False
для версий, которые не складываются правильно; фильмографические ссылки на Ideone, которые показывают, что это не соответствует требованиям 2.7.3 и 3.2.3. В этих версиях создаваемые кортежи не делят свои элементы с другими константами:
import dis
def f():
a, b = 257, 257
print(a is b)
print(f.__code__.co_consts)
#>>> (None, 257, (257, 257))
n = f.__code__.co_consts[1]
n1 = f.__code__.co_consts[2][0]
n2 = f.__code__.co_consts[2][1]
print(id(n), id(n1), id(n2))
#>>> (148384292, 148384304, 148384496)
Опять же, это не связано с изменением того, как объекты распакованы; это только изменение того, как объекты хранятся в co_consts
.