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.attr
muestra "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
object
tipo es inmutable y no se pueden agregar nuevos atributos? Esto parece que tendría más sentido.object
instancias de clase, no enobject
clase.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
object
qué 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
pympler
proyecto (puede obtenerlo a través de svn desde aquí ), podemos hacer algunas medidas ...:>>> from pympler import asizeof >>> asizeof.asizeof({}) 144 >>> asizeof.asizeof(23) 16
No querría que cada uno
int
ocupe 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 unadint
instancia puede tener atributos arbitrarios, pero que pagar un costo de un espacio para que la flexibilidad.Entonces, ¿qué pasaría si quisieras
int
s 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 dosint
s, uno elself
y 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 laclass
declaració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
,tuple
y en Python 3 tambiénint
.__dict__
, ¿su clase debe tener un__slot__
atributo?Sub
tiene el__dict__
atributo y el objeto no, siendo queSub
heredaobject
, ¿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 comoasizeof
parece la salida. (asizeof
no 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
object
no tiene un__dict__
.object
es la clase base de todos los tipos, incluidosint
ostr
. Por lo tanto, todo lo que les proporcioneobject
será 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.SimpleNamespace
para esto.fuente
Simplemente se debe a la optimización.
Los dictados son relativamente grandes.
>>> import sys >>> sys.getsizeof((lambda:1).__dict__) 140
La 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