Что это за изменения распаковка поведение вместо python2 на Питон3

Python 2.7.8

a = 257
b = 257
a is b # False

a, b = 257, 257
a is b # False

Python 3.4.2

a = 257
b = 257
a is b # False

a, b = 257, 257
a is b # True

Я знаю, что это, вероятно, не влияет на правильность программы, но это ошибка меня немного. Может кто-нибудь дать некоторое представление об этой разнице в распаковке?

2 ответов


это поведение, по крайней мере, частично связано с тем, как интерпретатор делает постоянное сворачивание и как REPL выполняет код.

во-первых, помните, что CPython сначала компилирует код (в AST, а затем байт-код). Затем он оценивает байткод. Во время компиляции скрипт ищет неизменяемые объекты и кэширует их. Он также дедуплицирует их. Так что если он видит

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. The 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 также; это не из-за какого-либо атрибута распаковки; они просто поместите в тот же блок компиляции.

возвращает False для версий, которые не складываются должным образом;filmor ссылки на 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.


Я думаю, что это на самом деле случайно, так как я не могу воспроизвести поведение в Python 3.2.

существует эта проблема http://bugs.python.org/issue11244 это вводит CONST_STACK чтобы исправить проблемы с постоянными кортежами с отрицательными числами, которые не оптимизируются (посмотрите на патчи против peephole.c, который содержит оптимизатор Python).

это, по-видимому, также привело к данному поведению. Все еще глядя в это:)