Entonces, estaba jugando con Python mientras respondía esta pregunta , y descubrí que esto no es válido:
o = object()
o.attr = 'hello'
debido a un AttributeError: 'object' object has no attribute 'attr'. Sin embargo, con cualquier clase heredada del objeto, es válida:
class Sub(object):
pass
s = Sub()
s.attr = 'hello'
La impresión s.attrmuestra "hola" como se esperaba. ¿Por qué es este el caso? ¿Qué especifica la especificación del lenguaje Python que no se pueden asignar atributos a los objetos vanilla?
python
attributes
language-design
Smashery
fuente
fuente

objecttipo es inmutable y no se pueden agregar nuevos atributos? Esto parece que tendría más sentido.objectinstancias de clase, no enobjectclase.Respuestas:
Para soportar la asignación de atributos arbitrarios, un objeto necesita un
__dict__: un dictado asociado con el objeto, donde se pueden almacenar atributos arbitrarios. De lo contrario, no hay ningún lugar donde colocar nuevos atributos.Una instancia de
objectqué no llevar un__dict__- si lo hiciera, antes de que el problema de la dependencia circular horribles (ya quedict, como casi todo lo demás, hereda deobject;-), esto sería cargar a cada objeto en Python con un diccionario, lo que significaría una sobrecarga de muchos bytes por objeto que actualmente no tiene o necesita un dict (esencialmente, todos los objetos que no tienen atributos asignables arbitrariamente no tienen o necesitan un dict).Por ejemplo, usando el excelente
pymplerproyecto (puede obtenerlo a través de svn desde aquí ), podemos hacer algunas medidas ...:>>> from pympler import asizeof >>> asizeof.asizeof({}) 144 >>> asizeof.asizeof(23) 16No querría que cada uno
intocupe 144 bytes en lugar de solo 16, ¿verdad? -)Ahora, cuando haces una clase (heredando de lo que sea), las cosas cambian ...:
>>> class dint(int): pass ... >>> asizeof.asizeof(dint(23)) 184... el
__dict__se añade ahora (además, un poco más sobrecarga) - por lo que unadintinstancia puede tener atributos arbitrarios, pero que pagar un costo de un espacio para que la flexibilidad.Entonces, ¿qué pasaría si quisieras
ints con solo un atributo adicionalfoobar...? Es una necesidad poco común, pero Python ofrece un mecanismo especial para ese propósito ...>>> class fint(int): ... __slots__ = 'foobar', ... def __init__(self, x): self.foobar=x+100 ... >>> asizeof.asizeof(fint(23)) 80... no es bastante tan pequeño como una
int, que importa! (o incluso los dosints, uno elselfy otro elself.foobar- el segundo se puede reasignar), pero seguramente mucho mejor que adint.Cuando la clase tiene el
__slots__atributo especial (una secuencia de cadenas), entonces laclassdeclaración (más precisamente, la metaclase predeterminadatype) no equipa cada instancia de esa clase con un__dict__(y por lo tanto la capacidad de tener atributos arbitrarios), solo un finito , conjunto rígido de "ranuras" (básicamente lugares que pueden contener una referencia a algún objeto) con los nombres de pila.A cambio de la flexibilidad perdida, se obtiene una gran cantidad de bytes por ejemplo (probablemente significativas sólo si tiene millones y millones de casos gallivanting alrededor, pero, no son casos de uso para eso).
fuente
__slots__no funcionan con los tipos de longitud variable tales comostr,tupley en Python 3 tambiénint.__dict__, ¿su clase debe tener un__slot__atributo?Subtiene el__dict__atributo y el objeto no, siendo queSubheredaobject, ¿cómo se__module__agrega ese atributo (y otros similares ) en la herencia? Puede ser que esta sea una nueva pregunta__dict__solo se crea la primera vez que se necesita, por lo que la situación del costo de la memoria no es tan simple comoasizeofparece la salida. (asizeofno sabe cómo evitar la__dict__materialización). Puede ver que el dict no se materializa hasta que sea necesario en este ejemplo , y puede ver una de las rutas de código responsable de la__dict__materialización aquí .Como han dicho otras personas que respondieron, an
objectno tiene un__dict__.objectes la clase base de todos los tipos, incluidosintostr. Por lo tanto, todo lo que les proporcioneobjectserá una carga para ellos también. Incluso algo tan simple como opcional__dict__necesitaría un puntero adicional para cada valor; esto desperdiciaría 4-8 bytes adicionales de memoria para cada objeto en el sistema, por una utilidad muy limitada.En lugar de hacer una instancia de una clase ficticia, en Python 3.3+, puede (y debe) usar
types.SimpleNamespacepara esto.fuente
Simplemente se debe a la optimización.
Los dictados son relativamente grandes.
>>> import sys >>> sys.getsizeof((lambda:1).__dict__) 140La mayoría (quizás todas) de las clases que se definen en C no tienen un dictado para la optimización.
Si observa el código fuente , verá que hay muchas comprobaciones para ver si el objeto tiene un dict o no.
fuente
Entonces, investigando mi propia pregunta, descubrí esto sobre el lenguaje Python: puedes heredar de cosas como int, y ves el mismo comportamiento:
>>> class MyInt(int): pass >>> x = MyInt() >>> print x 0 >>> x.hello = 4 >>> print x.hello 4 >>> x = x + 1 >>> print x 1 >>> print x.hello Traceback (most recent call last): File "<interactive input>", line 1, in <module> AttributeError: 'int' object has no attribute 'hello'Supongo que el error al final se debe a que la función de adición devuelve un int, por lo que tendría que anular funciones como
__add__esta para conservar mis atributos personalizados. Pero todo esto ahora tiene sentido para mí (creo), cuando pienso en "objeto" como "int".fuente
Es porque el objeto es un "tipo", no una clase. En general, todas las clases que se definen en extensiones C (como todos los tipos de datos incorporados y cosas como matrices numpy) no permiten la adición de atributos arbitrarios.
fuente
__dict__, por las razones dadas por Alex Martelli.https://docs.python.org/3/library/functions.html#object :
fuente
Esta es (IMO) una de las limitaciones fundamentales de Python: no puede volver a abrir las clases. Sin embargo, creo que el problema real es causado por el hecho de que las clases implementadas en C no se pueden modificar en tiempo de ejecución ... las subclases sí, pero no las clases base.
fuente