Pregunta sobre clases

Chema Cortes py en ls-l.org
Lun Ago 1 10:49:43 CEST 2005


Marcelo Barbero escribió:
> Sabía que Python permite crear variables tal como lo dicen, pero me parecía
> un poco "peligroso" dejar crear variables nuevas en una clase (sobre todo si
> uno le erra con el nombre de la variable, por un simple error de tipeado).
> 
> De todas maneras, me parece que mi preocupación no era vana. El uso de
> __slots__ demuestra que es un problema potencial que merece tenerse en
> cuenta, sobre todo porque las clases tienen su propio espacio de nombres.

El problema tiene más "intríngulis" del que parece. El uso de __slots__
está ideado por temas de optimización de recursos, no para limitar el
interface de la clase frente a los errores de escritura como sugieres.

Me explico mejor, aunque ya sé que es algo complicado:

En python, cada objeto tiene asociado un diccionario de nombres, que es
como se implementa su "espacio de nombres". Además de este diccionario,
a cada objeto se le asocia una referencia débil (weakref) consigo mismo.

Con las nuevas clases (las que derivan de la clase 'object') todos los
atributos/métodos se implementan mediante lo que se conoce como
"descriptores". El diccionario de nombres no es ya necesario, aunque
figura por compatibilidad y para poder seguir creando atributos
dinámicamente. Si tienes que crear miles y miles de instancias de una
misma clase es un desperdicio de memoria, de ahí que con __slots__
puedas indicar explícitamente los atributos que va a tener la instancia
(no hace falta que estos atributos estén declarados al declarar la clase).

Aún así, sigue siendo posible indicar que quieres el diccionario de
nombres (sin weakrefs) ó lo contrario, que quieres el atributo
'__weakref__' pero no el diccionario de nombre.

Por ejemplo:

>>> class P(object):
...    __slots__=["a","__dict__"]
...
>>> p=P()
>>> p.a=0
>>> p.aa=0  #no da error
>>> p.__dict__
{ "aa": 0 }
>>> #no figura 'a' en el diccionario
>>> hasattr(p, "__weakref__")
False


Insisto en que __slots__ no tiene que ver con el "interface". Por
ejemplo, el interface creado con __slots__ no sería hereditario "puro":

>>> class P(object):
... 	__slots__=["a"]
... 	
>>> class Q(P):pass
...
>>> Q.__slots__
['a']
>>> p=P()
>>> q=Q()
>>> p.aa=1
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
AttributeError: 'P' object has no attribute 'aa'
>>> q.aa=1 #no da error

>>> class W(P):
...    __slots__=[]
...
>>> W.__slots__
[]
>>> w=W()
>>> w.a=1 #no da error
>>> w.aa=1
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
AttributeError: 'W' object has no attribute 'aa'



En cuanto a las referencias débiles, es mucho más largo de contar (a ver
si algún día saco tiempo para hacerlo). Brevemente, en el atributo
__weakref__ se almacena una única referencia débil al objeto siguiendo
el patrón "singleton". Si usamos __slots__ y no incluímos el atributo
"__weakref__" será imposible hacer referencias débiles sobre este objeto:

>>> import weakref
>>> class P(object):
...   __slots__=["a"]
...
>>> x=weakref.ref(P())
Traceback (most recent call last):
  File "<interactive input>", line 1, in ?
TypeError: cannot create weak reference to 'P' object

>>> class P(object):
...   __slots__=["a","__weakref__"]
...
>>> x=weakref.ref(P()) #no da error




Más información sobre la lista de distribución Python-es