structures functions dict data comprehension python python-2.7 python-3.x difference iterable-unpacking

functions - ¿Qué ocurre con este cambio de comportamiento de desempaquetado de Python2 a Python3?



python list functions (2)

Ayer me encontré con esta extraña diferencia de desempaquetado entre Python 2 y Python 3, y no encontré ninguna explicación después de una rápida búsqueda en Google.

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

Sé que probablemente no afecte la corrección de un programa, pero me molesta un poco. ¿Alguien podría dar algunas ideas sobre esta diferencia en el desempaque?


Creo que esto es en realidad por accidente, ya que no puedo reproducir el comportamiento con Python 3.2.

Existe este problema http://bugs.python.org/issue11244 que introduce un CONST_STACK para solucionar problemas con tuplas constantes con números negativos que no se optimizan (consulte los parches contra peephole.c , que contiene las ejecuciones del optimizador de Python).

Esto parece haber llevado también al comportamiento dado. Sigo investigando esto :)


Este comportamiento, al menos en parte, tiene que ver con la forma en que el intérprete realiza el plegado constante y la forma en que el REPL ejecuta el código.

Primero, recuerde que CPython primero compila el código (para AST y luego el bytecode). Luego evalúa el bytecode. Durante la compilación, el script busca objetos que son inmutables y los almacena en caché. También los deduplica. Así que si ve

a = 257 b = 257

almacenará ayb contra el mismo objeto:

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

Tenga en cuenta el LOAD_CONST 1 . El 1 es el índice en co_consts :

f.__code__.co_consts #>>> (None, 257)

Así que estos dos cargan el mismo 257 . ¿Por qué no ocurre esto con:

$ 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

?

Cada línea en este caso es una unidad de compilación separada y la deduplicación no puede pasar a través de ellas. Funciona de manera similar a

compile a = 257 run a = 257 compile b = 257 run b = 257 compile a is b run a is b

Como tal, estos objetos de código tendrán cachés constantes únicos. Esto implica que si eliminamos el salto de línea, devuelve True :

>>> a = 257; b = 257 >>> a is b True

De hecho, este es el caso de ambas versiones de Python. De hecho, esto es exactamente por qué

>>> a, b = 257, 257 >>> a is b True

devuelve True también; no es por ningún atributo de desempaquetado; Simplemente se colocan en la misma unidad de compilación.

Esto devuelve False para las versiones que no se pliegan correctamente; Filmor enlaza con Ideone, que muestra este fallo en 2.7.3 y 3.2.3. En estas versiones, las tuplas creadas no comparten sus elementos con las otras constantes:

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)

Una vez más, sin embargo, esto no se trata de un cambio en cómo se desempaquetan los objetos; es solo un cambio en la forma en que los objetos se almacenan en co_consts .