[Michael McLay]
I was suprised by a change to the order of evaluation of members in the new object type. I haven't found an explanation for why the change was made.
Read PEP 252, paying special attention to the section containing: When a dynamic attribute (one defined in a regular object's __dict__) has the same name as a static attribute (one defined by a meta-object in the inheritance graph rooted at the regular object's __class__), the static attribute has precedence if it is a descriptor that defines a __set__ method (see below); otherwise (if there is no __set__ method) the dynamic attribute has precedence. In other words, for data attributes (those with a __set__ method), the static definition overrides the dynamic definition, but for other attributes, dynamic overrides static. Rationale: we can't have a simple rule like "static overrides dynamic" or "dynamic overrides static", because ...
... With the new slots mechanism the order has been reversed. The class level dictionary is searched and then the slots are evaluated.
I should hope so! The *point* of __slots__ (which is what you're really talking about, not the general concept of "slots") is that the class, not the object, is responsible for doing the attribute name->storage_address mapping, and in intended use an object of a class with __slots__ doesn't even have a __dict__ (each __slot__ attribute is allocated at a fixed offset from the start of the object, saving tons of storage).
class C(object): ... __slots__ = ['a']
Now objects of type C don't have a dict: storage for one attribute 'a' is allocated directly in C objects.
c = C() c.__dict__ Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object has no attribute '__dict__'
You can set and get 'a':
c.a = 12 c.a 12
C.a is a special beast:
C.a <member 'a' of 'C' objects>
What makes it special isn't that it came from __slots__, though, but that it has a __set__ method (reread the quoted text above until your eyes bleed <wink>: this is a deadly simple protocol, so simple that it can be hard to understand at first (shades of the metaclass hook and continuations, there)):
dir(C.a) ['__class__', '__delattr__', '__doc__', '__get__', '__getattribute__', ^^^^^^^ C.a.__get__ is called when c.a is referenced.
'__hash__', '__init__', '__name__', '__new__', '__objclass__', '__reduce__', '__repr__', '__set__', '__setattr__', '__str__'] ^^^^^^^ C.a.__set__ is called when c.a is bound or del'ed. Objects of C type can't grow new attributes:
c.b =12 Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object has no attribute 'b'
class B(object): __slots__ = ['a','b','c']
b = B() b.a = 4 b.a 4 B.a = 6
Here you overwrote the descriptor that allows b.a to mean something sensible (you nuked the <member 'a' of 'B' objects> thingie that maps 'a' to its storage address). Now B.a is an ordinary class attribute, and remember that b doesn't have a __dict__ (which you asked for, by using __slots__; you're not required to use __slots__).
b.a 6 b.a = 8 Traceback (most recent call last): File "<pyshell#61>", line 1, in ? b.a = 8 AttributeError: 'B' object attribute 'a' is read-only
I agree it's an odd msg, but I'm not sure it can do better easily: by overwriting B.a (which was nuts -- you're exploring pathologies here, not intended usage), you've left b as an object with an 'a' attribute inherited from its class, but also as an object that can't grow new attributes of its own. Python looks at "hmm, I *can't* set 'a', but I do *have* an 'a'", and comes up with "read-only". Try your example again without using __slots__ (you do *not* want __slots__ if you intend an object's namespace to be dynamic -- __slots__ announces that you guarantee the set of object attributes is fixed at class creation time):
class B(object): pass ... b = B() b.a = 4 B.a = 6 b.a 4 b.a = 8 b.a 8 B.a 6
IOW, don't use new features if you don't want new semantics, and things look much the same. If you want __slots__, though, there was no way to get its effect prior to 2.2 short of writing an ExtensionClass in C.