Estoy tratando de llenar un vacío en mi comprensión de cómo funcionan los objetos y las clases de Python.
Una instancia de object simple no admite la asignación de atributos de ninguna manera:
> object().a = 5 # or > setattr(object(), 'a', 5) AttributeError: 'object' object has no attribute 'a' Supongo que esto se debe a que una instancia de object simple no posee un atributo __dict__ :
> '__dict__' in dir(object()) False Sin embargo, una instancia de object simple tiene un atributo __setattr__ definido, por lo que esto es un poco confuso para mí:
> '__setattr__' in dir(object()) TrueUna instancia de una clase vacía normal, por otro lado, tiene plena capacidad de asignación de atributos:
class B(object): pass > B().a = 5 > setattr(B(), 'a', 5) Mi pregunta es: ¿qué diferencia inherente entre una instancia de object y una instancia de class B(object) permite que esta última tenga atributos asignables, si B hereda directamente del object ?
La clase object() es como una partícula fundamental del universo de python, y es la clase base (o bloque de construcción) para todos los objetos (leer todo) en Python. Como tal, el comportamiento establecido es lógico, ya que no todos los objetos pueden (o deben) tener atributos arbitrarios establecidos. Por ejemplo, no tendría sentido si un objeto NoneType pudiera tener atributos establecidos y, al igual que object() , un objeto None tampoco tiene un atributo __dict__ . De hecho, la única diferencia entre los dos es que un objeto None tiene un atributo __bool__ . Por ejemplo:
n = None o = object() type(n) >>> <class 'NoneType'> set(dir(n)) - set(dir(o)) >>> {'__bool__'} isinstance(n, object) >>> True bool(n) >>> False La herencia del object es automática y, al igual que cualquier otro medio de herencia, uno puede agregar sus propios métodos y atributos de clase al elemento secundario. Python agrega automáticamente el atributo __dict__ para tipos de datos personalizados como ya mostró.
En resumen, es mucho más fácil agregar el atributo __dict__ de un objeto que quitarlo para los objetos que no tienen atributos de escritura personalizados (es decir NoneType ).
Actualización basada en el comentario:
comentario original:
Entonces, ¿sería seguro asumir que es
__setattr__el que verifica la existencia de__dict__y genera una excepción en consecuencia? – bool3max
En CPython, la lógica detrás de object.__setattr__(self, name, value) se implementa mediante Objects/object.c _PyObject_GenericSetAttrWithDict (consulte el código fuente de CPython ). Específicamente, parece que si el argumento del name es una cadena, entonces se verifica el objeto en busca de un objeto __dict__ en una de sus ranuras y se asegura de que esté "listo" (consulte esta línea ).
El estado de preparación del objeto está determinado por PyType_Ready() , descrito brevemente y citado aquí :
Definir un tipo de Python en C implica completar los campos de una estructura PyTypeObject con los valores que le interesan. Llamamos a cada uno de esos campos una "ranura".
Una vez que la definición está lista, la pasamos a la función PyType_Ready(), que hace varias cosas, incluida la exposición de la mayor parte de la definición de tipo al mecanismo de búsqueda de atributos de Python.