[Python-Dev] Change in evaluation order in new object model

Michael McLay mclay@nist.gov
Fri, 2 Nov 2001 17:06:00 -0400


On Friday 02 November 2001 01:32 pm, Tim Peters wrote:
> [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).

Yes, that is one of the reasons I want to use them.  

I've read the paragraph and my eyes are bleeding.  I'm still trying to 
understand why the definition in that pargraph cased the order to be reverse. 
Why idoes the dynamic class attribute overriding an instance attribute?  The 
attributes added by __slots__ have __set__ and __get__ methods so it should 
take precedence according to the definition. The __slots__ names could have 
been used prior to the names in the dictionary in the class, just like the 
__dict__ in an old class instance object was searched prior to the __dict__ 
in the class definition.  

> >
> > >>> 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__).

Do you consider it a good thing that a class attribute can be introduced 
outside of the class definition?  Given the nature of the changes being 
introduced with the new type system I would think the ability to add names to 
a class dictionary from outside the class would be turned off for the new 
type classes.  If not for all types then at least for cases where the 
__slot__ mechanism is being used to define members. 

The slots mechanism also allows the same name to be used twice in the 
definition of a class. 

>>> class B(object):
    __slots__ = ['a','b','c','c']

I suspect this is going to cause a slot to be created for the first 'c' and 
then covered up by the second 'c'.

>> class B(object):
    __slots__ = ['a','b','c']
   
>>> class C(B):
	__slots__ = ['d','a']

In this example the same thing will happen when names are redefined in a 
subclass.

> > >>> 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), 

Why not make the overwriting of B.a generate an error message?

> 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

If I don't want the object's namespace to be dynamic then the class in which 
it is defined should not be dynamic either.

>
> 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.

I was looking for ways in which people could mess data iin objects from 
outside of those objects. The current semantics will allow people to do dumb 
thinks that will break code silently rather than raising an error. I need and 
like the new semantics.  I have extending them to allow optional type 
checking.  This will eliminate piles of ugly classes that wrap attribute 
access with isinstance type checks if they are added to the member descriptor 
and handled automatically by the set and get functions.