Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question about accessing class-attributes.

277 views
Skip to first unread message

Bjorn Pettersen

unread,
Apr 29, 2003, 8:15:43 PM4/29/03
to
> From: Alex Martelli [mailto:al...@aleax.it]
>
> Bjorn Pettersen wrote:
>
> >> From: Alex Martelli [mailto:al...@aleax.it]
> > [...]
>
> [special-methods problem snipped]
>
> Ah yes, I see now! Yes, functions such as len() rely on slots in
> the type object, e.g. as you've noticed:
>
[...]
> zone of the code. Maybe you can post this as a bug in 2.3 beta 1
> on sourgeforge (ideally showing where in the docs it defines the
> semantics that it then doesn't respect) so we can get this looked
> at by the few people who really DO grasp these parts...;-). There
> is probably some sound implementation reason for the current
> behavior, but if so it should be better documented, I think.

SF 729913. I have not been able to find an exact description of the
lookup rules for new-style classes anywhere.

> > The relevance this has to super, is that you couldn't do a
> > (contrived) example like:
> >
> > for item in super(MyClass, self):
> > ...
> >
> > (in general, you can't pass super(MyClass, self) to a
> > function expecting a MyClass instance, even if MyClass
> > defines no new behavior...)
>
> Yes, of course not -- super(MyClass, self) is NOT an instance
> of MyClass (isn't THAT why you're calling super...?).

I'm also missing an exact description of what super() returns. It looks
like it's an object that when you call method 'm', invokes the proper
super class' method 'm', and if that 'm' invokes any otherm method,
'm2', the original objects definiton 'm2' is used. So it looks like it
should be substitutable, or at least a first class object, but as you
said: "of course not". Am I missing some documentation, or is it
unreasonable of me to think it should work this way (what way should it
work... exactly)?


> > Perhaps a more realistic exmaple?:
> >
> > class Secure(object):
> > def __init__(self, name):
> > self.name = name
> >
> > def __getattr__(self, attr):
> > return getSecureMethod(self.name, attr, getUserPrivileges())
> >
> > class LogFoo(Secure):
> > def __init__(self):
> > Secure.__init__(self, 'SalaryDB')
> >
> > def foo(self):
> > print 'calling foo'
> > return super(LogFoo, self).foo()
> >
> > lf = LogFoo()
> > print lf.foo()
>
> Sorry, I don't get it. How is this related to metaclasses...?

Not terribly obvious was it... :-) And it's a bad example too. The
thinking was that if Super's metaclass could intercept special methods
through it's __getattr__, the object returned from super() could look
more like a regular object. That Super.__getattr__ doesn't check for a
__getattr__ in the __mro__ chain is a problem, but a separate one (sorry
for the confustion).

-- bjorn

Alex Martelli

unread,
Apr 30, 2003, 4:01:05 AM4/30/03
to
On Wednesday 30 April 2003 02:15 am, Bjorn Pettersen wrote:
...

> > is probably some sound implementation reason for the current
> > behavior, but if so it should be better documented, I think.
>
> SF 729913. I have not been able to find an exact description of the
> lookup rules for new-style classes anywhere.

Good point. I guess the language reference DOES need to provide
"language lawyers" with those!


> > Yes, of course not -- super(MyClass, self) is NOT an instance
> > of MyClass (isn't THAT why you're calling super...?).
>
> I'm also missing an exact description of what super() returns. It looks
> like it's an object that when you call method 'm', invokes the proper
> super class' method 'm', and if that 'm' invokes any otherm method,
> 'm2', the original objects definiton 'm2' is used. So it looks like it

Again, this needs to go somewhere in the docs, but the effect of

super(aclass, aninst).amethod()

SHOULD be the same as anotherclass.amethod(aninst) where
anotherclass is the first class following aclass in the mro of aninst's
class that defines amethod. Note that the *lookup* for amethod
CANNOT use aclass itself, BUT the argument PASSED to amethod
MUST be aninst -- or else how would super enable "cooperative
superclass method calling"?

Given
sup = super(aclass, aninst)
if sup were an instance of aclass, then the lookup for sup.amethod
would start in aclass, thus the typical use case such as:
def amethod(self):
print 'before'
super(thisclass, self).amethod()
print 'after'
would just recurse endlessly (well, until that raises an exception;-)
making it pretty useless. But also, unless aninst were the instance
passed to anotherclass.amethod, this too would inhibit the intended
effect -- i.e. in the typical diamond inheritance structure:

class base(object):
def amethod(self):
print 'base.amethod'

class der1(base):
def amethod(self):
print 'der1.amethod'
super(der1, self).amethod()

class der2(base):
def amethod(self):
print 'der2.amethod'
super(der2, self).amethod()

class der12(der1, der2): pass

class der21(der2, der1): pass

der12().amethod()
der21().amethod()

how would we obtain the required sequence of prints in both
cases?

I'm NOT claiming this is perfectly stabilized yet nor that all
subtle bugs have yet been shaken off -- I'm just trying to
explain why _I_ don't find this behavior at all surprising.


> should be substitutable, or at least a first class object, but as you

Well it IS a first-class object, surely? You can pass it as an argument,
return it as a function's result, use it as a value in a list or dict --
aren't these the characteristics DEFINING "first-class object"?

> said: "of course not". Am I missing some documentation, or is it
> unreasonable of me to think it should work this way (what way should it
> work... exactly)?

I wouldn't be surprised to find out that the "language lawyer" levels
of docs haven't been written down anywhere yet -- i.e. that there
is nothing much beyond descrintro or the short paragraph under
http://www.python.org/dev/doc/devel/lib/built-in-funcs.html that
describes super. But as for 'reasonableness', I dunno -- I do not
understand how you would expect 'super' to work to enable its basic
use case of cooperative superclass method calling and yet NOT have

super(aclass, aninst).amethod()

behave just the same as

anotherclass.amethod(aninst)


There may be a good design idea hiding in your expectations -- as
long as it doesn't break super's fundamental use case -- and so I
am quite keen on understanding them! Indeed if we were able to
classify the current behavior as "a bug" in some respect, AND give
a patch that breaks no currently designed-in behavior, yet "fixes the
bug" and makes super more useful to you, then that's the kind of
fix that COULD go in 2.3 beta 2 (new functionality won't, but in
some sense it might be considered "a bug" if super violates the
normal expectations of clearly-advanced users such as you).

Maybe we should break this thread in two -- one about metaclasses
and one about super -- with suitable subjects... I do not understand
where the 'bugs' of the two may be related nor actually what they
really have to do with "accessing class-attributes" except in the
vaguest, broadest sense.


> > > Perhaps a more realistic exmaple?:
> > >
> > > class Secure(object):
> > > def __init__(self, name):
> > > self.name = name
> > >
> > > def __getattr__(self, attr):
> > > return getSecureMethod(self.name, attr, getUserPrivileges())
> > >
> > > class LogFoo(Secure):
> > > def __init__(self):
> > > Secure.__init__(self, 'SalaryDB')
> > >
> > > def foo(self):
> > > print 'calling foo'
> > > return super(LogFoo, self).foo()
> > >
> > > lf = LogFoo()
> > > print lf.foo()
> >
> > Sorry, I don't get it. How is this related to metaclasses...?
>
> Not terribly obvious was it... :-) And it's a bad example too. The
> thinking was that if Super's metaclass could intercept special methods
> through it's __getattr__, the object returned from super() could look
> more like a regular object. That Super.__getattr__ doesn't check for a
> __getattr__ in the __mro__ chain is a problem, but a separate one (sorry
> for the confustion).

"the object returned from super" DOES look a lot "like a regular
object", except it has a custom metaclass that ensures its fundamental
behavior -- attribute lookups starts AFTER the given class, but, if and
when any bound-methods are built, the instance they're bound to is
the given one. We can't break THAT, I think.

I think that, currently, if you want the functionality you seem to be
after in this example, you must ensure that superclass Secure has,
in its class dict at the time type.new is called to build it, explicit
entries for all specialmethods of your interest -- that way, the slots
will be filled in (perhaps with special-purpose descriptors that do
the just-in-time lookup you may want). But, for methods that you
look up explicitly, a metaclass's __getattr__ (NOT of course the
__getattr__ of the CLASS on which you're looking up -- that only
applies to lookups on INSTANCES of that class) should work fine.

Yes, maybe we should separate super-issues and metaclass-issues
and find a sharp example in each case of where reasonably expected
behavior from docs, and observed behavior in 2.3 beta 1, differ. I
think it does not need to be a "reasonable use case": that would be
needed if we were arguing for EXTRA functionality of course -- but
then it would go in 2.4 so there's no hurry, to say the least. If we're
arguing there are BUGS, so the fixes could go in 2.3, then just a
way to show up each bug clearly should suffice (hopefully).


Alex


Michele Simionato

unread,
Apr 30, 2003, 10:25:00 AM4/30/03
to
"Bjorn Pettersen" <BPett...@NAREX.com> wrote in message news:<mailman.105166200...@python.org>...
> SF 729913. I have not been able to find an exact description of the
> lookup rules for new-style classes anywhere.
>
> I'm also missing an exact description of what super() returns. It looks
> like it's an object that when you call method 'm', invokes the proper
> super class' method 'm', and if that 'm' invokes any otherm method,
> 'm2', the original objects definiton 'm2' is used. So it looks like it
> should be substitutable, or at least a first class object, but as you
> said: "of course not". Am I missing some documentation, or is it
> unreasonable of me to think it should work this way (what way should it
> work... exactly)?
> <snip> The

> thinking was that if Super's metaclass could intercept special methods
> through it's getattr , the object returned from super() could look
> more like a regular object. That Super. getattr doesn't check for a
> getattr in the mro chain is a problem, but a separate one (sorry

> for the confustion).
>
> -- bjorn

super seems to be quite subtle, indeed. Unfortunately, the only documentation
is Guido's essay, which does not describe the internal details of the
implementation, it shows only a Python implementation, as you know already.

I think I understand your point, now. It is like in the following example:

class B(object):
def __getattr__(self,somename):
return somename

class C(B):
pass

b=B()
c=C()

print b.name # works
print c.name # works
print super(C,c).__getattr__('name') # "means" B.__getattr__(c,'name'); works
print super(C,c).name # does not work

"Means" does not mean it is *literally* equivalent. If it was so,
this would work:

class M(type):
def __getattr__(cls,somename):
return somename

class B(object):
__metaclass__=M
def __getattr__(self,somename):
return somename

class C(B):
pass

b=B()
c=C()

print B.name # works
print C.name # works
print b.name # works
print c.name # works
print super(C,c).__getattr__('name') # works
print super(C,c).name # does not work, it is not equivalent to B.name !

super(C,c) is a proxy to the methods of class B, passing to them
the instance c, but it does not see if the lookup on B has been
altered by a custom metaclass overriding __getattr__

Not sure if this is a bug or a design choice, anyway. You should post to
python-dev. I am more bothered by more trivial issues with super, like the
following one:

class C(object):
pass

C.s=super(C)

help(C)

Traceback (most recent call last):
File "<stdin>", line 6, in ?
File "/usr/local/lib/python2.3/site.py", line 293, in __call__
return pydoc.help(*args, **kwds)
File "/usr/local/lib/python2.3/pydoc.py", line 1539, in __call__
self.help(request)
File "/usr/local/lib/python2.3/pydoc.py", line 1575, in help
else: doc(request, 'Help on %s:')
File "/usr/local/lib/python2.3/pydoc.py", line 1368, in doc
pager(title % desc + '\n\n' + text.document(object, name))
File "/usr/local/lib/python2.3/pydoc.py", line 279, in document
if inspect.isclass(object): return self.docclass(*args)
File "/usr/local/lib/python2.3/pydoc.py", line 1122, in docclass
lambda t: t[1] == 'method')
File "/usr/local/lib/python2.3/pydoc.py", line 1057, in spill
name, mod, object))
File "/usr/local/lib/python2.3/pydoc.py", line 280, in document
if inspect.isroutine(object): return self.docroutine(*args)
File "/usr/local/lib/python2.3/pydoc.py", line 1145, in docroutine
realname = object.__name__
AttributeError: 'super' object has no attribute '__name__'

Here is the simple fix. I have submitted the bug report already.

class Super(super):
def __init__(self,cls,obj=None):
super(Super,self).__init__(cls,obj)
self.__name__='super(%s)' % cls.__name__

class C(object):
pass

C.s=Super(C)

help(C)

--------------------------------------------------------------------------
Help on class C in module __main__:

class C(__builtin__.object)
| Methods defined here:
|
| s = super(C)(...)
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __dict__ = <dictproxy object>
|
| __weakref__ = <attribute '__weakref__' of 'C' objects>


Notice the advantage with the present implementation of super as a
class: you can subclass it and correct its flaws ;)

HTH,

Michele

Michele Simionato

unread,
Apr 30, 2003, 3:48:58 PM4/30/03
to
Alex Martelli <al...@aleax.it> wrote in message news:<mailman.1051689777...@python.org>...
> <snip> Indeed if we were able to

> classify the current behavior as "a bug" in some respect, AND give
> a patch that breaks no currently designed-in behavior, yet "fixes the
> bug" and makes super more useful to you, then that's the kind of
> fix that COULD go in 2.3 beta 2 (new functionality won't, but in
> some sense it might be considered "a bug" if super violates the
> normal expectations of clearly-advanced users such as you).
> <snip>

> Maybe we should break this thread in two -- one about metaclasses
> and one about super -- with suitable subjects... I do not understand
> where the 'bugs' of the two may be related nor actually what they
> really have to do with "accessing class-attributes" except in the
> vaguest, broadest sense.
> <snip>

> Yes, maybe we should separate super-issues and metaclass-issues
> and find a sharp example in each case of where reasonably expected
> behavior from docs, and observed behavior in 2.3 beta 1, differ. I
> think it does not need to be a "reasonable use case": that would be
> needed if we were arguing for EXTRA functionality of course -- but
> then it would go in 2.4 so there's no hurry, to say the least. If we're
> arguing there are BUGS, so the fixes could go in 2.3, then just a
> way to show up each bug clearly should suffice (hopefully).
>
>
> Alex

I do agree with you. Speaking about wanted functionality (maybe for
2.4)
I would like very much a __superclass__ attribute. Example: now I can
do
(NOTICE: in p23b)

class B(object): pass
class C(B): pass
c=C()
super(C,c).__thisclass__() #=> C
super(C,c).__self_class__() #=> C

I would like to have an additional attribute (not a method)
returning the superclass of C, in this case B:

super(C,c).__superclass__ #=> B

For instance I could do

b=super(C,c).__superclass__()

returning an instance of B.

I think Bjorn would we happy with this, too.

Certainly the documentation in
http://www.python.org/dev/doc/devel/lib/built-in-funcs.html

has to be changed. It says:

"""
super(type[object-or-type])

Return the superclass of type. If the second argument is omitted
the super object returned is unbound. If the second argument is an
object, isinstance(obj, type) must be true. If the second argument is
a type, issubclass(type2, type)
must be true. """


But it DOES not return the superclass! I admit I myself at the
beginning
was confused.


Michele

Michele Simionato

unread,
Apr 30, 2003, 4:23:44 PM4/30/03
to
Bjorn Pettersen wrote:
> > Michele Simionato wrote:
> > I must say, however, that len(S) is not *literally*
> > type(S).__len__(S):
>
> Yes, that was one of my problems. Where did you find this rule? I can't
> find a real reference to it anywhere. For classic classes lookup is
> o.__class__.__dict__['attr'].__get__(o.__class__, o) for regular
> methods, which sort of looks like the above, but...

Never found any official reference except postings from Alex Martelli,
and
maybe I have read that in the Nutshell, too (Alex, correct me if I am
wrong!)
I do agree with you that the rule does not work in the case you pointed
out;
not sure if it is a bug or intended behaviour.

> [...]
> > I did not understood your point in the other thread, I didn't imagine
> > you where thinking about meta-metaclasses. As you see, there are
> > situations where they may be useful, i.e. in the customization of
> > metaclasses, but most people don't think at this level yet ;)
>
> I wasn't (or at least wasn't trying to :-). They're facinating beasts
> though -- a semantic system with recursion in its axioms ;-)
>
> -- bjorn

With meta-metaclasses you may implement type(S).__len__(S) but
I see now that your point is more with len(S) that does not
recognizes '__len__' methods provided indirectly via __getattr__ .
You can always do that

class Super(super):
def __getattr__(self,somename):
return somename

or even overrides __getattribute__ and implement an entirely
different version of super. Anyway, this does not solve your
problem.

It seems to me more a problem of len that of super,
in the sense that len(x) is not *literally* type(x).__len__(x)
(idem for __getitem__ in the other thread; I would expect the
same for str and repr, I should check ...)

--
Michele Simionato - Dept. of Physics and Astronomy
210 Allen Hall Pittsburgh PA 15260 U.S.A.
Phone: 001-412-624-9041 Fax: 001-412-624-9163
Home-page: http://www.phyast.pitt.edu/~micheles/

Bjorn Pettersen

unread,
Apr 30, 2003, 4:00:47 PM4/30/03
to
> From: Michele Simionato [mailto:mi...@pitt.edu]
>
[..]
> I am sure Alex is going to answer, but since you asked me a related
> question in another thread ...

Sorry, not trying to ignore you, just trying to figure out where the
disconnect was :-)

> Finally, now I see where is the source of the confusion:
>
> len(S) fails because meta.__len__ is NOT defined in
> you first example. The point is that __getattr__ works on instances of
> meta, NOT on meta itself, therefore S.__len__ is recognized thanks

Well, the whole point of a meta class is that it's "instances" are
regular classes, i.e. the class of a class is called the metaclass...

[meta-meta example...]

> I must say, however, that len(S) is not *literally*
> type(S).__len__(S):

Yes, that was one of my problems. Where did you find this rule? I can't
find a real reference to it anywhere. For classic classes lookup is
o.__class__.__dict__['attr'].__get__(o.__class__, o) for regular
methods, which sort of looks like the above, but...

[...]

Alex Martelli

unread,
Apr 30, 2003, 6:31:05 PM4/30/03
to
<posted & mailed>

Michele Simionato wrote:

> Bjorn Pettersen wrote:
>> > Michele Simionato wrote:
>> > I must say, however, that len(S) is not *literally*
>> > type(S).__len__(S):
>>
>> Yes, that was one of my problems. Where did you find this rule? I can't
>> find a real reference to it anywhere. For classic classes lookup is
>> o.__class__.__dict__['attr'].__get__(o.__class__, o) for regular
>> methods, which sort of looks like the above, but...
>
> Never found any official reference except postings from Alex Martelli,

Well, my posts aren't *official* references, of course.

> and
> maybe I have read that in the Nutshell, too (Alex, correct me if I am
> wrong!)

Actually this is a tidbit I chose to explain textually instead (p. 87):
"In the new-style object model, implicit use of special methods always
relies on the class-level binding of the special method, if any", and
then a short example of the difference between classic and new-style
in this regard.

In the earlier part about the *classic* object model, p. 78, I do give:

x.__class__.__dict__['name'](x, arg)

as being "just like" x.name(arg), but the previous paragraph does say
I'm talking of "a typical method call" and that 'name' is "naming one
of x's methods (a function-valued attribute of x's class)", so the
issue of __get__ [which wouldn't work in 2.1 -- I'm trying to cover
BOTH 2.1 and 2.2 in the part about the classic object model!] does
not need to enter the picture.

> I do agree with you that the rule does not work in the case you pointed
> out;
> not sure if it is a bug or intended behaviour.

Me neither. It MIGHT be an "unavoidable [or too-hard-to-avoid:-)]
implementation restriction", too. But it does need to be documented
even in that case, at least as long as it holds, I believe.


> With meta-metaclasses you may implement type(S).__len__(S) but
> I see now that your point is more with len(S) that does not
> recognizes '__len__' methods provided indirectly via __getattr__ .
> You can always do that
>
> class Super(super):
> def __getattr__(self,somename):
> return somename
>
> or even overrides __getattribute__ and implement an entirely
> different version of super. Anyway, this does not solve your
> problem.

Right. The problem is that the special-method names must be
in the class dictionary at the time type.__new__ runs for it,
in order for the slots in the type object to be set. They may
be set in various ways, but won't be set at all if the name
isn't in the class dictionary [or the name or slot gets
inherited from one of the class's bases] at the right time.


> It seems to me more a problem of len that of super,
> in the sense that len(x) is not *literally* type(x).__len__(x)
> (idem for __getitem__ in the other thread; I would expect the
> same for str and repr, I should check ...)

I think all of these built-ins go right for the jugular, i.e.,
the appropriate slot in the type object. And I do NOT think that
is "a problem"... as long as the slot is set "correctly". I do
wonder if the current non-setting in such advanced cases CAN be
deemed "correct", though.


Alex

Alex Martelli

unread,
Apr 30, 2003, 6:47:43 PM4/30/03
to
Michele Simionato wrote:
...

> class B(object): pass
> class C(B): pass
> c=C()
> super(C,c).__thisclass__() #=> C
> super(C,c).__self_class__() #=> C
>
> I would like to have an additional attribute (not a method)
> returning the superclass of C, in this case B:

*blink* and if "class D(E, F, G, H, I):", what would "the
superclass" *BE*?

Is what you're looking for something like you could obtain
by, e.g.:

class metasc(type):
def __new__(meta, name, bases, clasdict):
if bases: clasdict['__superclass__'] = bases[0]
else: clasdict['__superclass__'] = None
return type.__new__(meta, name, bases, clasdict)

??? If so, I fail to see the usefulness. Otherwise,
could you please explain better?


> Certainly the documentation in
> http://www.python.org/dev/doc/devel/lib/built-in-funcs.html
>
> has to be changed. It says:
>
> """
> super(type[object-or-type])
>
> Return the superclass of type. If the second argument is omitted
> the super object returned is unbound. If the second argument is an
> object, isinstance(obj, type) must be true. If the second argument is
> a type, issubclass(type2, type)
> must be true. """
>
> 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? Maybe by "superclass" you mean "base", a common
usage, but then the singular "the superclass" would be absurd.

I'm not sure what the practical use case for the 2nd argument being
a type is:

>>> class A(object): a = 23
...
>>> class B(object): a = 45
...
>>> class C(A, B): a = 67
...
>>> C.a
67
>>> super(A, C).a
45

but I don't know how one would get, from C, some object whose
attribute named 'a' is worth 23. So, I guess I just don't
understand this part of super's behavior.


Alex

Bjorn Pettersen

unread,
May 1, 2003, 1:47:33 AM5/1/03
to
> From: Alex Martelli [mailto:al...@aleax.it]

[superclass vs. baseclass]

> I'm not sure what the practical use case for the 2nd argument being
> a type is:
>
> >>> class A(object): a = 23
> ...
> >>> class B(object): a = 45
> ...
> >>> class C(A, B): a = 67
> ...
> >>> C.a
> 67
> >>> super(A, C).a
> 45
>
> but I don't know how one would get, from C, some object whose
> attribute named 'a' is worth 23. So, I guess I just don't
> understand this part of super's behavior.

Well, you wouldn't have cooperative super calls without a diamond
hierarchy, so in this case never <wink>.

The use case is of course our old friend, the classmethod:

class T(object):
def foo(cls):
print 'T.foo'
foo = classmethod(foo)

class Left(T):
def foo(cls):
super(Left, cls).foo()
print 'Left.foo'
foo = classmethod(foo)

class Right(T):
def foo(cls):
super(Right, cls).foo()
print 'Right.foo'
foo = classmethod(foo)

class B(Left, Right):
def foo(cls):
super(B, cls).foo()
print 'B.foo'
foo = classmethod(foo)

B.foo()

-- bjorn

Michele Simionato

unread,
May 1, 2003, 1:17:16 PM5/1/03
to
Alex Martelli <al...@aleax.it> wrote in message news:<joYra.44343$K35.1...@news2.tin.it>...
> Michele Simionato wrote:

> > I would like to have an additional attribute (not a method)

> > returning the superclass . <snip>


> > Certainly the documentation in
> > http://www.python.org/dev/doc/devel/lib/built-in-funcs.html
> >
> > has to be changed. It says:
> >
> > """
> > super(type[object-or-type])
> >
> > Return the superclass of type. If the second argument is omitted
> > the super object returned is unbound. If the second argument is an
> > object, isinstance(obj, type) must be true. If the second argument is
> > a type, issubclass(type2, type)
> > must be true. """
> >
> > 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 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.

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

def ancestor(C,S=None):
"""Returns the ancestors of the first argument with respect to the
MRO of the second argument. If the second argument is None, then
returns the MRO of the first argument."""
if C is object:
raise TypeError("There is no superclass of object")
elif S is None or S is C:
return list(C.__mro__)
elif issubclass(S,C): # typical case
mro=list(S.__mro__)
return mro[mro.index(C):] # compute the ancestors from the MRO of S
else:
raise TypeError("S must be a subclass of C")

The superclass is the first ancestor, ancestor(C,S)[1].
I would like super to have a reference to it. Useful at least
for pedagogical purposes, if you want to understand the methods
of which class super will be a proxy to.

Alex Martelli

unread,
May 1, 2003, 7:00:07 PM5/1/03
to
Michele Simionato wrote:

> Alex Martelli <al...@aleax.it> wrote in message
> news:<joYra.44343$K35.1...@news2.tin.it>...
>> Michele Simionato wrote:
>
>> > I would like to have an additional attribute (not a method)
>> > returning the superclass . <snip>

...


> 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

Well yes, but how does class C's attribute (not a method) get the
slightest idea about using the MRO of S rather than the MRO of any
other subclass of C?

> the ancestors of A with respect to the MRO of S:
>
> def ancestor(C,S=None):
> """Returns the ancestors of the first argument with respect to the
> MRO of the second argument. If the second argument is None, then
> returns the MRO of the first argument."""
> if C is object:
> raise TypeError("There is no superclass of object")
> elif S is None or S is C:
> return list(C.__mro__)
> elif issubclass(S,C): # typical case
> mro=list(S.__mro__)
> return mro[mro.index(C):] # compute the ancestors from the MRO
> of S
> else:
> raise TypeError("S must be a subclass of C")
>
> The superclass is the first ancestor, ancestor(C,S)[1].

Maybe, given S, but you asked for *AN ATTRIBUTE OF C*. So
would that attribute be identically equal to C.__bases__[0]?
WHY???

> I would like super to have a reference to it. Useful at least
> for pedagogical purposes, if you want to understand the methods
> of which class super will be a proxy to.

I don't see any pedagogical utility in claiming there is one
distinguished superclass. The super will quite happily be a
proxy to methods in other and completely unrelated classes if
the methods don't happen to exist directly in the one and
only one you want to present as "THE" superclass. Therefore,
it seems to me that said hypothetical class attribute would
only breed confusion and resulting errors.


Alex

Bjorn Pettersen

unread,
May 1, 2003, 7:41:03 PM5/1/03
to
> From: Michele Simionato [mailto:mi...@pitt.edu]
>
> Alex Martelli <al...@aleax.it> wrote in message
> news:<joYra.44343$K35.1...@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

Michele Simionato

unread,
May 2, 2003, 10:48:47 AM5/2/03
to
Alex Martelli <al...@aleax.it> wrote in message news:<XFhsa.30684$3M4.8...@news1.tin.it>...

> <snip various objections not related to what I am trying to say>

> I don't see any pedagogical utility in claiming there is one
> distinguished superclass. The super will quite happily be a
> proxy to methods in other and completely unrelated classes if
> the methods don't happen to exist directly in the one and
> only one you want to present as "THE" superclass. Therefore,
> it seems to me that said hypothetical class attribute would
> only breed confusion and resulting errors.
>
>
> Alex

Clearly, we are having some misunderstanding ( I don't know at
which level). I do think the concept of superclass (even in a multiple
inheritance situation) is well defined, useful and not confusing at all.
Let me try to explain.

Consider your example:



class A(object): a = 23

class B(object): a = 45

class C(A, B): a = 67

which is the typical diamond diagram:


object
/ \
A B
\ /
C

Now we want to explain to the newbie what super(A, C).a returns.

To this aim, we look at the MRO of C, which is the list [C,A,B,object].
Then, we define the superclass of A with respect to the MRO of C,
as the class following A in the list [C,A,B,object]. In this case the
superclass is B. Then we say to the newbie that super(A,C).a is *ideally*
equivalent to B.a and therefore it returns 45.

If super had a __superclass__ attribute I could say to the newbie

super(A,C).a <=> super(A,C).__superclass__.a <=> B.a

Of course, the equivalence would not be exact in subtle cases,
i.e. when B does not have an 'a' attribute, nor it inherits it,
but it has (or inherits) a __getattr__ defining 'a' (as Bjorn
pointed out)
Still in the typical case there would be equivalence.

I made my point clear, now ? At least, this is the way I followed
to teach to myself how super works.

Ciao,

Michele

Michele Simionato

unread,
May 2, 2003, 11:05:34 AM5/2/03
to
"Bjorn Pettersen" <BPett...@NAREX.com> wrote in message news:<mailman.105183252...@python.org>...
> <snip>

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

This is also the way I understand how class instantiation works. Maybe
we are both right (or both wrong ;)

> are you saying simply:
>
> definition("super class of C with respect to S") ::=

> S. mro [index(C)+1]
>

Yes.

> 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

Can you post actual code instead of BNF-like pseudocode, please ?
I would like to see a real implementation, if possible.


Michele

Michele Simionato

unread,
May 2, 2003, 11:17:36 AM5/2/03
to
I forgot ...

for bound methods the equivalence would be

super(A,c).m() <=> super(A,C).__superclass__.m(c) <=> B.m(c) where c=C()

i.e super automatically bind methods to instances when it is called
with the bound form.

Alex Martelli

unread,
May 2, 2003, 12:19:15 PM5/2/03
to
Michele Simionato wrote:
...

> superclass is B. Then we say to the newbie that super(A,C).a is *ideally*
> equivalent to B.a and therefore it returns 45.
>
> If super had a __superclass__ attribute I could say to the newbie
>
> super(A,C).a <=> super(A,C).__superclass__.a <=> B.a
>
> Of course, the equivalence would not be exact in subtle cases,
> i.e. when B does not have an 'a' attribute, nor it inherits it,
> but it has (or inherits) a __getattr__ defining 'a' (as Bjorn
> pointed out)
> Still in the typical case there would be equivalence.
>
> I made my point clear, now ? At least, this is the way I followed
> to teach to myself how super works.

Sorry, you still haven't made your point clear to me. What IS
the added value of that "__superclass__" attribute when its whole
point now seems to be that in the typical case it does nothing?

You asked for __superclass__ as an attribute of classes. Now it
seems that what you want is an extra attribute of the 'super'
object -- or is it, that you want an attribute of all classes
which is ALSO special in the super object? I really do not at
all understand what you're asking for nor what the point would be.


Alex

Bjorn Pettersen

unread,
May 2, 2003, 2:16:00 PM5/2/03
to
> From: Michele Simionato [mailto:mi...@pitt.edu]
>
> "Bjorn Pettersen" <BPett...@NAREX.com> wrote in message
> news:<mailman.105183252...@python.org>...

[ class, and instance instantiation...]

> This is also the way I understand how class instantiation works. Maybe
> we are both right (or both wrong ;)

I'm fairly sure the parts I listed are correct from reading the C code.

> > are you saying simply:
> >
> > definition("super class of C with respect to S") ::=
> > S. mro [index(C)+1]
> >
>
> Yes.

Why? What practical benefit would this possibly possess? Let's say I do:

sc = superclass(C,S)

what can I do with sc? Specifically,

sc.a != super(C,S).a

and

sc.a(S, ...) != super(C,S).a(...)

in fact the only thing I can think of that it can be used for is
recreating S.__mro__[index(C)+1:], which is in fact a useful concept
(see super context in the pseudo code I posted).

[pseudocode for super, and attribute lookup semantics...]

> Can you post actual code instead of BNF-like pseudocode, please ?
> I would like to see a real implementation, if possible.

Hmm... Why? (I can't see how implementation details would do anything
but obfuscate the issues)... You're asking me to implement a significant
part of an interpreter, because you don't like to read pseudo-code? Why
don't we try the other way first? You tell me what's unclear, and I'll
clarify.

-- bjorn

Michele Simionato

unread,
May 2, 2003, 4:42:15 PM5/2/03
to
Bjorn Pettersen wrote:
> > Let's say I do:
> >
> > sc = superclass(C,S)
> >
> > what can I do with sc? Specifically,
> >
> > sc.a != super(C,S).a
>because sc.a is an unbound attribute of the class sc or a bound
>attribute of sc.__metaclass__, while super(C,S).a is a bound method
>created from the first implementation in S.__mro__[index(C}+1:], which
>could potentially be sc but only as a special case, and S. There really
>isn't any equivalence

Not in a strict sense, yes, but look at what I said:

>If super had a __superclass__ attribute I could say to the newbie
>
>super(A,C).a <=> super(A,C).__superclass__.a <=> B.a
>
>Of course, the equivalence would not be exact in subtle cases,
>i.e. when B does not have an 'a' attribute, nor it inherits it,
>but it has (or inherits) a __getattr__ defining 'a' (as Bjorn
>pointed out)
>Still in the typical case there would be equivalence.

This is the typical case I had in mind (I am thinking about attributes,
not about methods)

def superclass(C,S):
mro=list(S.__mro__)
return mro[mro.index(C)+1]

class A(object): a=1
class B(object): a=2
assert super(A,C).a is superclass(A,C).a # okay
assert super(A,C) is B.a # okay

These are the tricky cases I am aware of, where there is no equivalence
(speaking for attributes only):

#tricky case #1

class A(object): a=2
class B(object):
class __metaclass__(type):
a=2

class C(A,B): pass

assert B.a is superclass(A,C).a # okay
super(A,C).a # attribute error


#tricky case #2

class A(object): a=2
class B(object):
class __metaclass__(type):
def __getattr__(cls,name):
if name=='a': return 2

class C(A,B): pass

assert B.a is superclass(A,C).a #okay
super(A,C).a # attribute error

In my view, one could say that for most uses super(A,C) can be seen as
a proxy to the methods of superclass(A,C)==B, and that the exceptions are
in rather special and advanced cases. But the fact that those case are special
and quite advanced is only a personal opinion, of course ;)

> It is simply that I can run actual code and experiment with it,
> I can try some test cases, etc. I cannot do that with pseudo-code.
> Often ideas that works on paper don't work in reality :-(

>Similarly you can run pseudo code, except you have to think about the
>process instead of relying on the answer a specific implementation gives
>you.

I disagree, you cannot run pseudo-code in your mind and believe that
it will run the same in the real word. But perhaps your mind is a
much better compiler than mine ;)

>What I'm talking about is language theory, i.e. how things are
>_supposed_ to work or a concreate description of how something _does_
>work. Arguing about details of a concrete implementation is both
>fruitless (I don't care how it works, I want to know how it should
>work), and time-consuming (to get anything runnable you'd have to
>implement _at_least_ and abstract syntax tree evaluator, and you'd have
>to get the ast's from somewhere... and it'd have to be for a large
>enough subset of the language that it was interesting).
>If you can't get the theory to hang together "on paper" first, you're
>going to end up hacking an endless series of special cases in your
>implementation -- with the result simply being a different implmentation
>with no more insight into how things should work than the origianl
>implementation.

We have different philosophies, then. Whereas I am a theoretical
physicists and I have also worked in mathematical physics and pretty
abstract stuff, when it comes to programming I am an eminently *practical*
person. I *never* start top-down, writing my design on the paper: I always
begin coding with a very little idea of what I want to do. It is the practical
implementation and experimentation that drives me to the final result, which
is necessarely always different from what I had in mind in the beginning.
I code *first*, put things on the paper later, when I have a working
implementation. That's why Python dynamism and rapid development
times fit my mind so well.

Cheers,

Michele

Michele Simionato

unread,
May 2, 2003, 5:14:12 PM5/2/03
to
Alex Martelli <al...@aleax.it> wrote in message news:<7Uwsa.33378$3M4.9...@news1.tin.it>...
>
> Sorry, you still haven't made your point clear to me. What IS
> the added value of that "__superclass__" attribute when its whole
> point now seems to be that in the typical case it does nothing?

Have a __superclass__ attribute of super would solve the
following tricky cases:

#tricky case #1

class A(object): a=2
class B(object):
class __metaclass__(type):
a=2

class C(A,B): pass

assert superclass(A,C).a is 2 # okay
super(A,C).a # attribute error


#tricky case #2

class A(object): a=2
class B(object):
class __metaclass__(type):
def __getattr__(cls,name):
if name=='a': return 2

class C(A,B): pass

superclass(A,C).a is 2 #okay
super(A,C).a # attribute error

However, I do agree that these are very special cases not worth big
consideration; moreover I can implement my own superclass()
function with two lines of code. Simply I thought the ability of
retrieving the superclass from super was handy ... since it seems
I am the only one who thinks so, and I can already do what I want with
little effort, I will retract my proposal ;)

> You asked for __superclass__ as an attribute of classes.

Ah! Here is the source of the misunderstanding: I *never* asked for
__superclass__ as an attribute of generic classes, this would be
pretty stupid, a synonymous of cls.__bases__[0], not doing what I
want. I see now why you were confused.

> Now it seems that what you want is an extra attribute of the 'super'
> object -- or is it, that you want an attribute of all classes
> which is ALSO special in the super object?

This is another point, completely unrelated to the previous one.
In my code, I do add a private attribute to all classes for help
with super, but I was not talking about that. But since you raised
the point ...

What I do is to add a private attribute __thisclass to any class via
a custom metaclass; then I can write code like


class C:
__metaclass__=KnowsItself
def __init__(self):
super(self.__thisclass,self).__init__()

where KnowsItself add to C an attribute C._C__thisclass==C in such a
wayt that the previous code is the same that

class C:
__metaclass__=KnowsItself
def __init__(self):
super(C,self).__init__()

More verbose, but I avoid the repeat to name C in super and I am obsessive
in avoiding duplication of code ... ;)


Michele

Bjorn Pettersen

unread,
May 4, 2003, 12:40:24 AM5/4/03
to
> From: Michele Simionato [mailto:mi...@pitt.edu]
>
> Bjorn Pettersen wrote:
> > > Let's say I do:
> > >
> > > sc = superclass(C,S)
> > >
> > > what can I do with sc? Specifically,
> > >
> > > sc.a != super(C,S).a
> >
> > because sc.a is an unbound attribute of the class sc or a bound
> > attribute of sc.__metaclass__, while super(C,S).a is a bound method
> > created from the first implementation in S.__mro__[index(C}+1:],
> > which could potentially be sc but only as a special case, and S.
> > There really isn't any equivalence
>
> Not in a strict sense, yes, but look at what I said:

Well, if they're not strictly equivalent, and with no definition of when
they'd not be equivalent, and only work for attributes, I'm failing to
see what purpose it could serve?

> > If super had a __superclass__ attribute I could say to the newbie
> > super(A,C).a <=> super(A,C).__superclass__.a <=> B.a

...<aside: why not super(A,C).__class__?>..

[..__getattr__ special case..]

> >Still in the typical case there would be equivalence.
>
> This is the typical case I had in mind (I am thinking about
> attributes, not about methods)

I'm not sure why you want to make a difference, but ok. Let me change
your example just a little:

def superclass(C,S):
# mro=list(S.__mro__) let's assume we're dealing with objects
instead
mro = list(S.__class__.__mro__)
return mro[mro.index(C)+1]

class T(object): a = 0
class A(T): pass
class B(T): a = 2
class C(A,B): a = 3
c = C()

> assert super(A,C).a is superclass(A,C).a # okay
> assert super(A,C) is B.a # okay

print super(C,c).a, superclass(C,c).a # 2 0

The key word here is _cooperative_. A singular superclass has no
meaning, you need the entire list of the remaining superclasses, in the
right order.

> These are the tricky cases I am aware of, where there is no
> equivalence (speaking for attributes only):

[...]

Could you give definitions instead of examples :-)

> In my view, one could say that for most uses super(A,C) can
> be seen as a proxy to the methods of superclass(A,C)==B, and

From above that's false, it is only true with single inheritance and
special cases of multiple inheritance. In a previous post I defined the
semantics of super (it would be helpful if you come up with something
more precise than "in most cases", e.g. listing when not etc.) by
introducing the concept of a super_context. You can define a working
version of your superclass in terms of that too:

# new version, fixing obj/class problem
def super_context(A1, A2):
if isinstance(A2, A1):
mro = list(A2.__class__.__mro__)
else:
mro = list(A2.__mro__)
remaining = mro[mro.index(A1)+1:]
return remaining

class superclass(object):
def __init__(self, cls, obj):
self.__ctxt__ = super_context(cls, obj)

def __getattr__(self, attr):
for cls in self.__ctxt__:
if attr in cls.__dict__:
return cls.__dict__[attr]
raise AttributeError(attr)

whith this definition you can also do:

superclass(Cls, obj).__getitem__(obj, key)

[..pseudocode vs working implementation..]

> We have different philosophies, then. Whereas I am a
> theoretical physicists and I have also worked in
> mathematical physics and pretty abstract stuff, when
> it comes to programming I am an eminently *practical*
> person.

[...]

But this isn't programming. This is semantic analysis which should be
very similar to theoretical physics (do you need a working experiment to
discuss a theory?)

-- bjorn

Michele Simionato

unread,
May 5, 2003, 7:54:42 AM5/5/03
to
Bjorn Pettersen wrote:

> <snip objections against the concept of superclass>


> > If super had a __superclass__ attribute I could say to the newbie
> > super(A,C).a <=> super(A,C).__superclass__.a <=> B.a

...<aside: why not super(A,C).__class__?>..

Because super(A,C).__class__ returns the class of the super object,
<type 'super'> not the superclass of A with respect to the MRO of C !

>[..__getattr__ special case..]

> >Still in the typical case there would be equivalence.
> >
> > This is the typical case I had in mind (I am thinking about
> > attributes, not about methods)
>
> I'm not sure why you want to make a difference, but ok. Let me change
> your example just a little:

> def superclass(C,S):
> # mro=list(S.__mro__) let's assume we're dealing with objects instead
> mro = list(S.__class__.__mro__)
> return mro[mro.index(C)+1]

> class T(object): a = 0
> class A(T): pass
> class B(T): a = 2
> class C(A,B): a = 3
> c = C()

>> assert super(A,C).a is superclass(A,C).a # okay
>> assert super(A,C) is B.a # okay

> print super(C,c).a, superclass(C,c).a # 2 0

> The key word here is _cooperative_. A singular superclass has no
> meaning, you need the entire list of the remaining superclasses, in the
> right order.

Okay, I see your point now, and I give up.

> superclass(Cls, obj).__getitem__(obj, key)

Right. .If you look at the implementation of Super I posted some time
ago (in another thread, I think), you will see that it is not very
dissimilar. This is a case were I had a working code but a wrong (or
at least, useless) concept of superclass in mind: you see the
difference between theory and practice! ;)

> [..pseudocode vs working implementation..]
>
> > We have different philosophies, then. Whereas I am a
> > theoretical physicists and I have also worked in
> > mathematical physics and pretty abstract stuff, when
> > it comes to programming I am an eminently *practical*
> > person.
> [...]

> But this isn't programming. This is semantic analysis which should be
> very similar to theoretical physics (do you need a working experiment to
> discuss a theory?)

I am not familiar with "semantic analysis" (I can read the BNF and
write down
the simplest production rules, but that' all). It seems to me quite
different from
theoretical physics and the branches of mathematics I am familiar
with.
And even in theoretical physics, you cannot believe *any* theory
without experiments. Strictly speaking, you cannot believe even when
you have
the experiments ;). You can formulate the theory without experiments,
that's true,
but you are never sure that the theory you formulated is fully
consistent, even at
the pure mathematical level (I have worked on these issues during my
Ph.D. ,
therefore I speak for experience).
The only thing you can say is that some theories are so obviously
stupid that
you don't need any experiment to refute them ;)

> -- bjorn

Cheers,
Michele

Bjorn Pettersen

unread,
May 5, 2003, 12:18:33 PM5/5/03
to
> From: Michele Simionato [mailto:mi...@pitt.edu]
>
> Bjorn Pettersen wrote:
>
> > <snip objections against the concept of superclass>
> > > If super had a __superclass__ attribute I could say to the newbie
> > > super(A,C).a <=> super(A,C).__superclass__.a <=> B.a
>
> ...<aside: why not super(A,C).__class__?>..
>
> Because super(A,C).__class__ returns the class of the super object,
> <type 'super'> not the superclass of A with respect to the MRO of C !

Which is a pure implementation detail (which is why I'd rather not
discuss implementation when we can't even agree on design). If your
superclass "returns the superclass of A ...", and super(A,C) is the
"super object" representing the context for calling a super method, why
wouldn't it make more sense for super(A,C).__class__ == superclass(A,C)?
(A.k.a., give an example where the current definition of
super(A,C).__class__ is useful, personally it's caused me mounds of
grief <wink>).

[.. exmple indicating singular superclass is insufficient ..]

> > The key word here is _cooperative_. A singular superclass has no
> > meaning, you need the entire list of the remaining
> > superclasses, in the right order.
>
> Okay, I see your point now, and I give up.

:-)

[.. reference to super_context in a previous post ..]

> > ..by introducing the concept of a super_context. You

architecture/construction, concept/implementation, design/code, ... :-)

> > [..pseudocode vs working implementation..]
> >
> > > We have different philosophies, then. Whereas I am a
> > > theoretical physicists and I have also worked in
> > > mathematical physics and pretty abstract stuff, when
> > > it comes to programming I am an eminently *practical*
> > > person.
> > [...]
>
> > But this isn't programming. This is semantic analysis which
> > should be very similar to theoretical physics (do you need
> > a working experiment to discuss a theory?)
>
> I am not familiar with "semantic analysis"

It's basically a fancy term for the "design" phase of language design
and, given an existing language, the analysis of what a particular
construct _means_ (this is the semantic part) in a particular context.
E.g. what happens to memory when the program contains "f(x)"? What is
the real (as opposed to visible) structure of the value created when
saying "class X:..."? Given "a.b", which rules define how b is found
(assuming it means attribute lookup)?

The analysis part is done in the "regular way". Start with a set of
atomic operations (axioms), provide formulaes building on these until
you can give formulaes for the constructs in a language (which end you
start at depends on the problem). If the formulaes are internally
consistent (i.e. any construct has exactly one meaning which is well
defined), you have an language that can be implemented as intended (as
opposed to "almost there, just need to hack around this special case").
This entire creation together defines what it means to run a specific
program written in your language (i.e. the language's semantics). So it
boils down to the creation of a pretty regular logic system, which, as
usual, is as sound as your axioms and your proofs :-)

> (I can read the BNF and write down the simplest production
> rules, but that' all).

Which is important. The point that most people (including me) miss the
first time around (especially coming from languages where classes aren't
considered objects), is that if we've defined OBJECT(...) to be the only
atomic operation creating a, let's call it, "compound value", then at
runtime "class X:..." must somehow boil down to OBJECT(..). The simpler
your definition of your atomic operations, the easier it is to create an
implementation for the language (in general).

The theory was originally defined by Plotkin in "A structural approach
to operational semanticts", 1981. If this interest you, see the first
three chapters of http://adam.wins.uva.nl/~x/sos/sos.pdf for an
introduction and references.

> It seems to me quite different from theoretical physics and the
> branches of mathematics I am familiar with. And even in theoretical
> physics, you cannot believe *any* theory without experiments.

Mea culpa. I was confusing theorems vs. theories vs. hypotheses.

> Strictly speaking, you cannot believe even when you have the
> experiments ;). You can formulate the theory without experiments,
> that's true, but you are never sure that the theory you formulated
> is fully consistent, even at the pure mathematical level (I have

> worked on these issues during my Ph.D., therefore I speak for
> experience).

Very true. Which is why even language theorists take the time to create
implmentations :-)

> The only thing you can say is that some theories are so obviously
> stupid that you don't need any experiment to refute them ;)

Yet, their proponents are often very adamant <wink>.

It's generally much more time efficient to discover whether something is
correct and consitent by discussing it in the abstract first, instead of
trying to tweak an implementation based on false assumptions to do the
right thing (which, I admit, you can do most of the time, but I would
never call the result aesthetically pleasing :-)

-- bjorn

0 new messages