2010-01-13 7 views
10

Duplicar posible:
Python “is” operator behaves unexpectedly with integersidentidad Python: trastorno de personalidad múltiple, necesita código de encogimiento

yo nos topamos con el siguiente Python weirdity:

>>> two = 2 
>>> ii = 2 

>>> id(two) == id(ii) 
True 
>>> [id(i) for i in [42,42,42,42]] 
[10084276, 10084276, 10084276, 10084276] 

>>> help(id) 
Help on built-in function id in module __builtin__: 

id(...) 
    id(object) -> integer 

    Return the identity of an object. This is guaranteed to be unique among 
    simultaneously existing objects. (Hint: it's the object's memory address.) 
  1. Es cada numbe ¿un objeto único?
  2. Son variables diferentes que tienen los mismos valores elementales (por ejemplo, dos, ii) el mismo objeto?
  3. ¿Cómo es la identificación de un número generado por Python?
  4. En el ejemplo anterior, ¿hay dos y dos punteros a una celda de memoria con el valor 2? Eso sería extremadamente extraño.

Ayuda a resolver esta crisis de identidad.

Algunos más weirdities:

>>> a,b=id(0),id(1) 
>>> for i in range(2,1000): 
    a,b=b,id(i) 
    if abs(a-b) != 12: 
    print('%i:%i -> %i' % (i,a,b)) 

El código anterior examina si los id de enteros consecutivos también son consecutivas, e imprime anomalías:

77:10083868 -> 10085840 
159:10084868 -> 10086840 
241:10085868 -> 10087840 
257:10087660 -> 11689620 
258:11689620 -> 11689512 
259:11689512 -> 11689692 
260:11689692 -> 11689548 
261:11689548 -> 11689644 
262:11689644 -> 11689572 
263:11689572 -> 11689536 
264:11689536 -> 11689560 
265:11689560 -> 11689596 
266:11689596 -> 11689656 
267:11689656 -> 11689608 
268:11689608 -> 11689500 
331:11688756 -> 13807288 
413:13806316 -> 13814224 
495:13813252 -> 13815224 
577:13814252 -> 13816224 
659:13815252 -> 13817224 
741:13816252 -> 13818224 
823:13817252 -> 13819224 
905:13818252 -> 13820224 
987:13819252 -> 13821224 

Tenga en cuenta que un patrón emerge de 413 en adelante. Tal vez se deba a alguna contabilidad vudú al comienzo de cada nueva página de memoria.

+1

¿Qué importancia tiene? ¿Porque lo preguntas? ¿Qué problema causa esto? No entiendo la pregunta. Por favor, aclara lo que está roto. –

+3

@ S.Lott Está tratando de profundizar su comprensión, ¿qué pasa con eso? A veces el viaje vale más que el destino. – Simon

+1

No entiendo la pregunta. No puedo ayudar a profundizar mi comprensión si la pregunta es opaca. –

Respuesta

4

Su cuarta pregunta, "en el ejemplo anterior, son dos y ii indicadores para una celda de memoria con el valor 2" que sería extremadamente extraño ", es realmente la clave para entender todo.

Si está familiarizado con idiomas como C, las "variables" de Python en realidad no funcionan de la misma manera. AC declaración de variables como:

int j=1; 
int k=2; 
k += j; 

dice, "compilador, reserva para mí dos áreas de la memoria, en la pila, cada una con espacio suficiente para guardar un número entero, y recuerda una como 'j' y el otro como" k '. Luego complete j con el valor' 1 'yk con el valor' 2 '. " En tiempo de ejecución, el código dice "tomar el contenido entero de k, agregar los contenidos enteros de j y almacenar el resultado de nuevo en k".

El código aparentemente equivalente en Python:

j = 1 
k = 2 
k += j 

dice algo diferente: "Python, buscar el objeto conocido como '1', y crear una etiqueta llamada 'j' que apunta a que mirar hacia arriba. el objeto conocido como '2', y crea una etiqueta llamada 'k' que lo señala. Ahora busca los puntos 'k' del objeto en ('2'), busca los puntos 'j' del objeto en ('1'), y señale 'k' al objeto resultante de realizar la operación 'agregar' en los dos. "

Desmontaje de este código (con los dis módulo) muestra esto muy bien:

2   0 LOAD_CONST    1 (1) 
       3 STORE_FAST    0 (j) 

    3   6 LOAD_CONST    1 (2) 
       9 STORE_FAST    1 (k) 

    4   12 LOAD_FAST    1 (k) 
      15 LOAD_FAST    0 (j) 
      18 INPLACE_ADD 
      19 STORE_FAST    1 (k) 

Así que sí, Python "variables" son etiquetas que apuntan a objetos, en lugar de contenedores que puede ser lleno de datos.

Las otras tres preguntas son todas variaciones sobre "¿cuándo crea Python un objeto nuevo a partir de un fragmento de código y cuándo reutiliza uno que ya tiene?". Este último se llama "internamiento"; pasa a enteros más pequeños y cadenas que se ven (a Python) como si fueran nombres de símbolos.

+1

"objeto conocido como '2', y crear una etiqueta llamada 'j'" que debería ser '1' –

+0

Gracias, tolomea - reparado! –

9

Los enteros entre -1 y 255 (?), Así como los literales de cadena, están internados. Cada instancia en la fuente en realidad representa el mismo objeto.

En CPython, el resultado de id() es la dirección en el espacio de proceso del PyObject.

+1

Esto solo es necesariamente cierto para CPython. E incluso allí, nadie ha garantizado que eso no cambie, incluso durante el congelamiento de la sintaxis. – jcdyer

+0

Es cierto. Solía ​​ser hasta 99 o más en versiones anteriores. –

2

Debe tener mucho cuidado con este tipo de investigaciones. Está investigando los aspectos internos de la implementación del lenguaje, y esos no están garantizados. La ayuda en id es puntual: el número será diferente para dos objetos diferentes, y lo mismo para el mismo objeto. Como detalle de implementación, en CPython es la dirección de memoria del objeto. CPython puede decidir cambiar este detalle en cualquier momento.

El detalle de enteros pequeños que están internados con el mismo tiempo de asignación también es un detalle que podría cambiar en cualquier momento.

Además, si cambia de CPython a Jython, o PyPy, o IronPython, todas las apuestas están desactivadas, a excepción de la documentación en id().

1

No todos los números son un objeto único, y el hecho de que algunos son un detalle de optimización del intérprete CPython. No confíe en este comportamiento. Para el caso, nunca use is para probar la igualdad. Solo use is si está absolutamente seguro de que necesita exactamente el mismo objeto.

+2

Advertencia: siempre debe usar 'is' cuando se compara con' None'. – jcdyer

+0

Y 'Ellipsis'. Aunque nunca tendrás que hacer eso. –

+3

La regla general, directamente de PEP-8, es "uso es cuando se compara con un objeto singleton". Eso significa None, True, False, Ellipsis, etc. Sin embargo, True y False generalmente se omiten por completo, para permitir que tenga lugar la coerción booleana. –

8

Todas las implementaciones de Python están totalmente permitidas para optimizar en cualquier medida (incluso .... ninguna; ;) la identidad y la asignación de objetos inmutables (como números, tuplas y cadenas) [[no such existe latitud para objetos mutables, como listas, dictados y conjuntos]].

Entre dos referencias a objetos inmutables a y b, toda la aplicación debe garantizar es:

  1. id(a) == id(b), conocido como a is b, siempre debe implicar a == b
  2. y por lo tanto a != b siempre debe implicar id(a) != id(b) También conocido como a is not b

Nota en particular hay no restricción, incluso para tipos inmutables, que a == b debe implicar a is b (es decir, que id(a) == id(b)). Solo None hace esa garantía (para que siempre pueda probar if x is None: en lugar de if x == None:).

Las implementaciones actuales de CPython aprovechan estos grados de libertad mediante "fusión" (que tiene una sola asignación, por lo tanto, un solo id, para enteros pequeños en un cierto rango y objetos incorporados de tipo inmutable cuyos literales aparecen más que una vez dentro de una función determinada (por ejemplo, si su función f tiene cuatro apariciones de literal 'foobar', todas se referirán a una sola instancia de la cadena 'foobar' dentro de las constantes de la función, ahorrando un poco de espacio en comparación con la implementación permitida que almacenaría cuatro pero copias separadas de esa constante).

Todas estas consideraciones de implementación son de poco interés para los programadores de Python (a menos que trabaje en una implementación de Python, o al menos algo estrechamente ligado a una implementación específica, como un sistema de depuración).

Cuestiones relacionadas