¿Por qué id ({}) == id ({}) e id () == id () en CPython?

¿Por qué CPython (ninguna pista sobre otras implementaciones de Python) tiene el siguiente comportamiento?

tuple1 = () tuple2 = () dict1 = {} dict2 = {} list1 = [] list2 = [] # makes sense, tuples are immutable assert(id(tuple1) == id(tuple2)) # also makes sense dicts are mutable assert(id(dict1) != id(dict2)) # lists are mutable too assert(id(list1) != id(list2)) assert(id(()) == id(())) # why no assertion error on this? assert(id({}) == id({})) # or this? assert(id([]) == id([])) 

Tengo algunas ideas de por qué, pero no puedo encontrar una razón concreta de por qué.

EDITAR

Para probar aún más el punto de Glenn y Thomas:

 [1] id([]) 4330909912 [2] x = [] [3] id(x) 4330909912 [4] id([]) 4334243440 

CPython es un objeto de recolección de basura tan pronto como sale del ámbito, por lo que el segundo [] se crea después de que se recostack el primer [] . Entonces, la mayoría de las veces termina en la misma ubicación de memoria.

Esto muestra lo que está sucediendo muy claramente (es probable que la salida sea diferente en otras implementaciones de Python):

 class A(object): def __init__(self): print "a", def __del__(self): print "b", # aabb False print A() is A() # abab True print id(A()) == id(A()) 

Cuando llama a id({}) , Python crea un dict y lo pasa a la función id . La función id toma su id (su ubicación de memoria), y tira el dictado. El dict se destruye. Cuando lo haces dos veces en rápida sucesión (sin que se creen otros dicts en el tiempo promedio), el dictamen que Python crea la segunda vez, utiliza el mismo bloque de memoria que la primera vez. (El asignador de memoria de CPython lo hace mucho más probable de lo que parece). Ya que (en CPython) id utiliza la ubicación de la memoria como la identificación del objeto, la identificación de los dos objetos es la misma. Obviamente, esto no ocurre si asigna el dictado a una variable y luego obtiene su id() , porque los dictados están vivos al mismo tiempo , por lo que su id tiene que ser diferente.

La mutabilidad no entra directamente en juego, pero los objetos de código que almacenan en caché tuplas y cadenas sí. En el mismo objeto de código (función, cuerpo de clase o módulo), se reutilizarán los mismos literales (enteros, cadenas y ciertas tuplas). Los objetos mutables nunca se pueden reutilizar, siempre se crean en tiempo de ejecución.

En resumen, la identificación de un objeto solo es única durante la vida útil del objeto . Una vez que se destruye el objeto, o antes de que se cree, otra cosa puede tener la misma ID.

no funciona de la misma manera en Jython …

 >>> id({}) 1 >>> id([]) 2 

¿Podría haber una optimización en curso donde los contenedores de uso común (es decir, vacíos) se “internen” para ahorrar en los costos de asignación?

Esto (en CPython) sugiere no:

 >>> def mutateid(obj): ... obj.append('x') ... print obj ... print id(obj) ... >>> mutateid([]) ['x'] 4299590472 >>> id([]) 4299590472 >>> 

El operador == en las listas y los dictados no comparan los ID de objeto para ver si son el mismo objeto; usar obj1 is obj2 para eso.

En su lugar, el operador == compara los miembros de la lista de dict para ver si son iguales.