__slots__ and class attributes

Steven Bethard steven.bethard at gmail.com
Thu Nov 3 13:57:51 EST 2005


Ewald R. de Wit wrote:
> I'm running into a something unexpected  for a new-style class
> that has both a class attribute and __slots__ defined. If the
> name of the class attribute also exists in __slots__, Python
> throws an AttributeError. Is this by design (if so, why)?
> 
> class A( object ):
> 	__slots__ = ( 'value', )
> 	value = 1
> 
> 	def __init__( self, value = None ):
> 		self.value = value or A.value
> 
> a = A()
> print a.value
> 
> 
> Traceback (most recent call last):
>   File "t1.py", line 8, in ?
>     a = A()
>   File "t1.py", line 6, in __init__
>     self.value = value or A.value
> AttributeError: 'A' object attribute 'value' is read-only

Check the documentation on __slots__[1]:

__slots__ are implemented at the class level by creating descriptors 
(3.3.2) for each variable name. As a result, class attributes cannot be 
used to set default values for instance variables defined by __slots__; 
otherwise, the class attribute would overwrite the descriptor assignment.

I agree that the error you get is a bit confusing.  I think this has to 
do with how the descriptor machinery works.  When you write something like
     a.value
where a is a class instance, Python tries to invoke something like:
     type(a).value.__get__(a)
Here's an example of that, working normallly:

py> class A(object):
...     __slots__ = ['value']
...     def __init__(self):
...         self.value = 1
...
py> a = A()
py> type(a).value
<member 'value' of 'A' objects>
py> type(a).value.__get__
<method-wrapper object at 0x0129A1B0>
py> type(a).value.__get__(a)
1

Now when you add a class attribute called 'value', you overwrite the 
descriptor.  So when Python tries to do the same thing (because your 
definition of __slots__ makes it assume that 'value' is a descriptor), 
the descriptor machinery raises an AttributeError:

py> class A(object):
...     __slots__ = ['value']
...     value = 1
...
py> a = A()
py> type(a).value
1
py> type(a).value.__get__
Traceback (most recent call last):
   File "<interactive input>", line 1, in ?
AttributeError: 'int' object has no attribute '__get__'

This AttributeError must be somehow caught by the __slots__ machinery 
and interpreted to mean that you tried to write to a read-only 
attribute.  The resulting error message is probably not what you want, 
but I don't know the source well enough to figure out whether or not a 
better error message could be given.


But why do you want a class level attribute with the same name as an 
instance level attribute?  I would have written your class as:

class A(object):
     __slots__ = ['value']
     def __init__(self, value=1):
         self.value = value

where the default value you put in the class is simply expressed as a 
default value to the __init__ parameter.

Steve

[1]http://docs.python.org/ref/slots.html



More information about the Python-list mailing list