2012-04-18 7 views
6

esto es con CPython 2.7.2 y 3.2.2.¿por qué algunas expresiones que hacen referencia a `` x.y`` cambian `` id (x.y) ``?

supongamos que definimos Class y obj de la siguiente manera.

class Class(object): 

    def m(self): 
     pass 

    @property 
    def p(self): 
     return None 

    @staticmethod 
    def s(): 
     pass 

obj = Class() 

versión corta

¿por qué el siguiente código de salida para cada Falseprint()?

print(Class.__dict__ is Class.__dict__) 
print(Class.__subclasshook__ is Class.__subclasshook__) 
print(Class.m is Class.m) 

print(obj.__delattr__ is obj.__delattr__) 
print(obj.__format__ is obj.__format__) 
print(obj.__getattribute__ is obj.__getattribute__) 
print(obj.__hash__ is obj.__hash__) 
print(obj.__init__ is obj.__init__) 
print(obj.__reduce__ is obj.__reduce__) 
print(obj.__reduce_ex__ is obj.__reduce_ex__) 
print(obj.__repr__ is obj.__repr__) 
print(obj.__setattr__ is obj.__setattr__) 
print(obj.__sizeof__ is obj.__sizeof__) 
print(obj.__str__ is obj.__str__) 
print(obj.__subclasshook__ is obj.__subclasshook__) 
print(obj.m is obj.m) 

(eso es en Python 2; para Python 3, omita el print() para Class.m y añadir otras similares para obj.__eq__, obj.__ge__, obj.__gt__, obj.__le__, obj.__lt__ y obj.__ne__)

y por qué, por el contrario, ¿El siguiente código muestra True para cada print()?

print(Class.__class__ is Class.__class__) 
print(Class.__delattr__ is Class.__delattr__) 
print(Class.__doc__ is Class.__doc__) 
print(Class.__format__ is Class.__format__) 
print(Class.__getattribute__ is Class.__getattribute__) 
print(Class.__hash__ is Class.__hash__) 
print(Class.__init__ is Class.__init__) 
print(Class.__module__ is Class.__module__) 
print(Class.__new__ is Class.__new__) 
print(Class.__reduce__ is Class.__reduce__) 
print(Class.__reduce_ex__ is Class.__reduce_ex__) 
print(Class.__repr__ is Class.__repr__) 
print(Class.__setattr__ is Class.__setattr__) 
print(Class.__sizeof__ is Class.__sizeof__) 
print(Class.__str__ is Class.__str__) 
print(Class.__weakref__ is Class.__weakref__) 
print(Class.p is Class.p) 
print(Class.s is Class.s) 

print(obj.__class__ is obj.__class__) 
print(obj.__dict__ is obj.__dict__) 
print(obj.__doc__ is obj.__doc__) 
print(obj.__module__ is obj.__module__) 
print(obj.__new__ is obj.__new__) 
print(obj.__weakref__ is obj.__weakref__) 
print(obj.p is obj.p) 
print(obj.s is obj.s) 

(eso es en Python 2; para Python 3, añadir similares print() s para Class.__eq__, Class.__ge__, Class.__gt__, Class.__le__, Class.__lt__ y Class.__ne__ y Class.m)

versión larga

si solicite id(obj.m) dos veces seguidas, luego obtenemos la misma identificación dos veces.

>>> id(obj.m) 
139675714789856 
>>> id(obj.m) 
139675714789856 

pero si nos preguntamos por id(obj.m), a continuación algunas expresiones que contienen referencias a obj.m, a continuación, id(obj.m) de nuevo, cambia el ID de veces (pero no siempre). si cambia, si pedimos id(obj.m) en otro momento, la identificación cambia algunas veces al valor original (pero no siempre). si no cambia, parece que la repetición de las expresiones intermedias hace que la ID alternará entre los dos valores.

aquí son algunos ejemplos en los que id(obj.m) no cambia:

>>> print(obj.m); id(obj.m) 
<bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 
139675714789856 
>>> obj.m is None; id(obj.m) 
False 
139675714789856 
>>> obj.m.__func__.__name__; id(obj.m) 
'm' 
139675714789856 
>>> obj.m(); id(obj.m) 
139675714789856 

aquí es un ejemplo en el que id(obj.m) cambios, a continuación, cambia de nuevo:

>>> obj.m; id(obj.m); id(obj.m) 
<bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 
139675715407536 
139675714789856 

aquí es un ejemplo en el que id(obj.m) cambios, entonces no cambia de nuevo:

>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675715407536 
139675715407536 

aquí es la misma expresión, que se repite un par de veces para demostrar el comportamiento alterna:

>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675714789856 
139675714789856 
>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675715407536 
139675715407536 
>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675714789856 
139675714789856 

así toda mi pregunta es

  • qué tipo de atributos podrían cambiar su identidad como un efecto secundario de las expresiones que no modifica esos atributos?
  • ¿qué tipo de expresiones desencadenan dichos cambios?
  • ¿Cuál es el mecanismo que causa dichos cambios?
  • ¿bajo qué condiciones se reciclan las identidades pasadas?
  • ¿por qué la primera identidad no se recicla indefinidamente, lo que evitaría toda esta complicación?
  • ¿Está documentado todo esto?

Respuesta

5

qué tipo de atributos pueden cambiar su identidad como un efecto secundario de expresiones que no modificar estos atributos?

Propiedades, o más precisamente objetos que implementan el descriptor protocol. Por ejemplo, Class.__dict__ no es dict sino dictproxy. Claramente, este objeto se genera de nuevo cada vez que se solicita. ¿Por qué? Probablemente para reducir la sobrecarga de crear el objeto hasta que sea necesario hacerlo. Sin embargo, este es un detalle de implementación. Lo importante es que __dict__ funciona como está documentado.

Incluso los métodos de instancia ordinarios se manejan mediante descriptores, lo que explica por qué obj.m is not obj.m. Curiosamente, si hace obj.m = obj.mpermanentemente, guarde esa envoltura de método en la instancia y luego obj.m is obj.m. :-)

¿qué tipos de expresiones desencadenan dichos cambios?

Cualquier acceso a un atributo puede activar el método __get__() de un descriptor, y este método siempre puede devolver el mismo objeto o devolver uno diferente cada vez.

¿Cuál es el mecanismo que causa tales cambios?

Propiedades/descriptores.

¿bajo qué condiciones se reciclan las identidades pasadas?

No estoy seguro de lo que quiere decir "reciclado." ¿Quiere decir "eliminados" o "reutilizados"? En CPython, el id de un objeto es su ubicación de memoria. Si dos objetos terminan en la misma ubicación de memoria en diferentes momentos, tendrán el mismo id. Por lo tanto, dos referencias que tienen el mismo iden diferentes momentos (incluso dentro de una sola declaración) no son necesariamente el mismo objeto. Otras implementaciones de Python usan reglas diferentes para generar id s. Por ejemplo, creo que Jython utiliza números enteros incrementales, que proporcionan más claridad en la identidad del objeto.

¿por qué la primera identidad no se recicla indefinidamente, lo que evitaría todas estas complicaciones?

Es de suponer que el uso de descriptores tenía alguna ventaja. El código fuente para el intérprete de Python está disponible; mira eso si quieres saber más detalles.

¿hay algo de esto documentado?

No. Estos son detalles de implementación específicos del intérprete CPython y no se debe confiar en ellos. Otras implementaciones de Python (incluidas las versiones futuras de CPython) pueden, y probablemente lo harán, comportarse de manera diferente. Hay diferencias significativas entre 2.xy 3.x CPython, por ejemplo.

2

Cuando escribe x.y se crea un método dependiente o no vinculado. Este es un nuevo objeto. Puede ir a cualquier lugar en la memoria. Si escribe x.y y no usa el resultado, su refcnt puede ir a cero. Esto significa que la memoria está disponible y puede ser utilizada por la próxima x.y, posiblemente en la misma ubicación, pero no necesariamente.

Tenga en cuenta que CPython ofrece muy pocas garantías sobre la identidad del objeto (es decir, se garantiza que solo hay una instancia de Ninguna); de lo contrario, gran parte de lo que está viendo son elecciones arbitrarias de implementación que pueden cambiar.

+0

La respuesta de @Kindall resuelve la mayor parte de mi pregunta, pero esto resuelve la subpregunta sobre las ID recicladas. gracias. – nisavid

Cuestiones relacionadas