The potential for optimizing the new class/type sytem in Python can be improved by adding an additional constraint to the name lookup rules. This new constrain only apply to the new name lookup rules for types that include __slots__ definitions so the change should not break backward compatibility.
- Any name defined by __slot__ is not allowed as a key in the type dict. This rule would eliminate special type attribute names, like __dict__ and __class__, from the names allowed in __slots__.
The constraint would also make it illegal to injected a name into the type dictionary from outside the class definition if that name is one of the names in _slots__.
In the following example the costraint would allow the assignment to B.d, but not the assignment to B.b. The assignment of 'c=5' would trigger a TypeError because the c is defined in __slots__. The '__dict__' in __slots__ would also trigger a type error because it is a special attribute name that is defined for all types.
a = 3 c = 5 __slots__ = ['b', '__dict__' , 'c'] def __init__(self): self.b = 4
B.d = 'fish' b = B() b.b
B.b = 3.2 b.b
b.b = 55
Traceback (most recent call last): File "<pyshell#29>", line 1, in ? b.b = 55 AttributeError: 'B' object attribute 'b' is read-only
The rule eliminates the need to look in the type dictionary if the name is defined in __slots__. The cryptic AttributeError message in the example would never occur.
A slightly more restrictive rule would extend the constraint to eliminate any addition of names to the type dict from outside of the class definition. This additional constraint would make the assignment to B.d illegal in the previous example. This rule would make it possible to know the complete list of all names that could be referenced by an object of the given type.
In the more complicated case, there's a conflict between names stored in the instance dict and names stored in the type dict. If both dicts have an entry with the same key, which one should we return? Looking at classic Python for guidance, I find conflicting rules: for class instances, the instance dict overrides the class dict, *except* for the special attributes (like __dict__ and __class__), which have priority over the instance dict.
- I resolved this with the following set of rules, implemented in PyObject_GenericGetAttr():
1. Look in the type dict. If you find a *data* descriptor, use its get() method to produce the result. This takes care of special attributes like __dict__ and __class__.
2. Look in the instance dict. If you find anything, that's it. (This takes care of the requirement that normally the instance dict overrides the class dict.)
3. Look in the type dict again (in reality this uses the saved result from step 1, of course). If you find a descriptor, use its get() method; if you find something else, that's it; if it's not there, raise AttributeError.