__slots__ and class attributes

  • Thread starter Ewald R. de Wit
  • Start date
E

Ewald R. de Wit

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
 
S

Steven Bethard

Ewald said:
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
 
E

Ewald R. de Wit

Steven said:
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.

Thanks for your explanation. The reason why I was doing it was
to have class-level defaults, so that one can easily adjust how
new instances will be made. I'm doing it now with capitilized
class attribute names to avoid the name clash.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top