Question about accessing class-attributes.

Bjorn Pettersen BPettersen at NAREX.com
Thu May 1 19:41:03 EDT 2003


> From: Michele Simionato [mailto:mis6 at pitt.edu] 
> 
> Alex Martelli <aleax at aleax.it> wrote in message 
> news:<joYra.44343$K35.1283700 at news2.tin.it>...
> > Michele Simionato wrote:
> 
[...]
> > > """
> > >  super(type[object-or-type])
> > > 
> > >       Return the superclass of type. If the second [...]
[...]
> > > 
> > > But it DOES not return the superclass! I admit I myself at the
> > > beginning was confused.
> > 
> > The documentation is surely imperfect (e.g. by making no reference
to
> > the mro, which is crucial in the case in which the 2nd argument is
> > there and is an instance of the first one).  But doesn't the second
> > sentence say explicitly that e.g. super(C) returns a "super object"
> > that is unbound? 

Yes. But I'd like it better if it said "bindable super object", as in an
object implementing a set of methods automatically belonging to a
category. (Similarly to e.g. file-like objects, the only thing special
here is that there's an explicit __get__). Or if that doesn't go over
well, at least add the definition of "unbound object" to LangRef 3.2,
close to "User defined methods", and make a link to it (making the
headings in 3.2 html targets(?) would be a good idea too). I'll go file
a doc bug.

> Yes, but the first sentence says it returns the superclass, therefore 
> from the second sentence one could infer that a super object is a 
> class (??). It is confusing, at least.

True. It's using terminology that means different things in different
languages, without defining it...

> > Maybe by "superclass" you mean "base", a common
> > usage, but then the singular "the superclass" would be absurd.
> 
> Given a class C and a subclass S, there is ONLY ONE superclass of C
> with respect to the MRO of S. I have a routine returning the list of
> the ancestors of A with respect to the MRO of S:

Well, let's see if I know Python well enough to explain what it's doing
or perhaps what it should be doing...

Given an algorithm for instantiating a class, and creating a class
object (i.e. at runtime when you see "class X(object): ..." the
following things happen, similarly with "X(..)":

   evaluate("class_declaration"):
       - create an empty class C with C::__dict__  set to an
         object of mapping type.
       - if there exists a metaclass declaration, set the
         attribute C::__class__, and C::__dict__['__metaclass__]
         to the metaclass otherwise set C::__class__ to type.
       ...
       - perform a C3 linearization on all the base classes
         and assign the result as a tuple to C::__dict__[__mro__].
       ...

   evaluate("instantiate(class_object)"):
       - create an empty object obj with obj::__dict__ set to an
         object of mapping type, and obj::__class__ to class_object.
       ...

are you saying simply:

   definition("super class of C with respect to S") ::= 
      S.__mro__[index(C)+1]

would you entertain dividing it up:

   linearized(C) ::=
      C.__mro__
   super_context(A1, A2) ::=
      remaining = A2.__mro__[index(A1)+1 : ]
      return remaining

   linear_bases(C) ::=   # more descriptive than ancestor?
      C.__mro__[1:]   
   superclass(A1, A2) ::=       # don't have a use case for this one
      super_context(A1, A2)[0]

then super could be defined easily (and I can't see any obvious problems
with it, although that might not mean much right now... :-):

  # Convenient shorthand, mainly to make them notationally
  # separate from classes and objects (to prevent recursive
  # deifnitions, can either be implemented as classes/objects
  # or C structs, etc.
  RECORD{class_tag}(v1,.., vn)

  super(C, obj) ::=
      context = super_context(C, obj)
      return RECORD{super_object}(context, obj) 
                with __class__ := obj::__class__

  super(C) ::=
      r = RECORD{unbound_super}(C)
      r.__get__(obj, _):
          return super(C, obj)
      return r
      
   obj.attr ::=
      if obj matches RECORD[super_object](context, obj)
          return _get__attribute(obj, context, attr)
      else:
          return _get_attribute(obj, linearized(obj::__class__), x)
          
   # len(), hash(), int(), ...
   special(obj) ::=
      # look it up using regular mechanism
      hookFn = _get_attribute(obj, linearized(obj::__class__),
'__special__')
      # tell imlementation how to call the hook
      call_special_function(hookFn)

  _compute_attribute(x, cls, attr) ::=
      returns cls::__dict__['__getattr__'](x, attr) if it has a value
      or raises AttributeError

  _atomic_get_attribute(x, cls, attr) ::=
      # how I think it should work (btw., type(x) is a red-herring).
      
      Return the first expression that has a value (or raise from
compute_attribute):
         if attr is normal:
            x::__dict__[attr]
            cls::__dict__[attr].__get__(x, cls)
            cls::__dict__[attr]
            compute_attribute(x, attr)
         if attr is special:
            cls::__dict__[attr]
            cls.__metaclass__::__dict__[attr].__get__(cls,
cls.__metaclass__) ##??
            cls.__metaclass__::__dict__[attr]
            compute_attribute(cls, attr)

  _get_attribute(x, linearization, attr):
      for cls in linearization:
          try:
              return _atomic_get_attribute(x, cls, attr)
          except AttributeError:
              pass
       raise AttributeError, attr

I'm not sure I got the descriptors right, so feel free to criticize...

-- bjorn





More information about the Python-list mailing list