> > > P.S. Note that there is an additional complication resulting from the > fact that functions are descriptors: > >>>> class C(dict): > ... pass > ... >>>> C.__iter__ > <slot wrapper '__iter__' of 'dict' objects> >>>> C().__iter__ > <method-wrapper object at 0x00E74A10> > > Even though the C instance is accessing the __iter__ function on the > class, it gets back a different value because descriptors return > different values depending on whether they are accessed from a > class or > an instance. I don't think you need to understand this to solve your > problem though, so I won't go into any more details unless you > think it > would be helpful.
I found your explanation very helpful. After reading it I went back and read my Nutshell book and realized that the explanation was in there but I didn't "get" it until now. Although I did find an exception to the rule for attribute writes. (See !Whoops below) If you would care to elaborate on the how the lookup differs with method descriptor it would be most appreciated. Mostly because it seems that having slots defined changes the method lookup as opposed to the variable lookup and apparently some of the type class variables are special enough that they have their own rules. This might help explain why it is that when I define __slots__, the behavior when writing an attribute is different for attributes that exist in the class versus attributes that exist in __slots__ versus attributes that do not exist at all. It is also different if the class attribute is a method vesus a variable. For example >>> class C(dict): ... __slots__ = ['a','b'] ... >>> c = C() >>> c.a Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: a So slot defined but not assigned gets error >>> c.a = 5 >>> c.a 5 OK here >>> c.c Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object has no attribute 'c' Surprise error gives no clue that slots is the reason for the error >>> c.c = 4 Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object has no attribute 'c' ditto Now the behavior is different for class variables and methods when slots defined versus when slots is not defined. >>> c.__iter__ = 4 Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object attribute '__iter__' is read-only >>> super(C,c).__iter__ = 4 Traceback (most recent call last): File "<stdin>", line 1, in ? TypeError: 'super' object has only read-only attributes (assign to .__iter__) >>> >>> c.__class__ = C >>> c.__class__ <class '__main__.C'> it let me assign it! But not shadowed >>> c.__dict__ Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object has no attribute '__dict__' !Whoops now I am confused again. Didn't you say > When "writing" an attribute (i.e. using the assignment statement), > Python does not try to do any namespace searching. Thus if you use > the > instance in an assignment statement, then it is the instance's > attributes that get modified, and if you use the class in an > assignment > statement, then it is the class's attributes that get modififed: Then why wasn't __class__ added to c.__dict__ ? Looks like namespace searching to me. So to cross check if slots is not defined >>> class C(dict): ... pass ... >>> c = C() >>> c.__iter__ = 1 >>> c.__dict__ {'__iter__': 1} >>> c.__class__ = C >>> c.__dict__ {'__iter__': 1} >>> try again a different way class B(C): ... pass ... >>> c.__class__ = B >>> c.__dict__ {'__iter__': 4} OK but maybe __class__ is magic, so I tried again >>> class C(dict): ... a = 0 ... >>> c = C() >>> c.a 0 >>> c.a = 4 >>> c.__dict__ {'a': 4} OK __class__ is special now with slots defined >>> class C(dict): ... __slots__ = ['b'] ... a = 0 ... >>> c = C() >>> c.a 0 >>> c.a = 4 Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object attribute 'a' is read-only >>> >>> C.a = 5 >>> c.a 5 >>> So the rule is that when __slots__ is defined class variables become read only. What if the class variable is included in __slots__ >>> class C(dict): ... __slots__ = ['b'] ... b = 1 ... >>> c = C() >>> c.b 1 >>> c.b = 2 Traceback (most recent call last): File "<stdin>", line 1, in ? AttributeError: 'C' object attribute 'b' is read-only So even though b is in slots I still can't create an instance variable by that name and shadow the class variable. It feels like the implementation of slots is half baked. Finally Since the "way" of python is that if an object does not have an attribute but you can assign it one then it creates one dynamically (in the same 'way' that if a variable does not exist is creates one upon assignment). Because slots break this paradigm then at the very least the error messages should point out that this object is using slots "so beware". For example I would prefer something like the following c.a AttributeError: Slot 'a' not yet assigned c.c AttributeError: No slot named 'c' on instance c.c = 4 AttributeError: No slot named 'c' on instance if change rule to not access class from instance when slots define c.__iter__ = 4 AttributeError: No slot named '__iter__' on instance or with current behavior AttributeError: No slot name '__iter__' class 'C' object attribute '__iter__' is read-only super(C,c).__iter__ = 4 TypeError: 'super' object has only read-only attributes (assign to .__iter__) -- http://mail.python.org/mailman/listinfo/python-list