super. could there be a simpler super?

K

Kerim Borchaev

Hello!

Always when I use "super" I create a code duplication because class
used as first arg to "super" is always the class where the method
containing "super" was defined in:
'''
class C:
def method(self):
super(C, self).method()
'''

Obviously the methods like the one below doesn't work "right";-)
'''
def super(self):
super(self.__class__, self)
class C:
def method(self):
super(self).method()
'''

Is it possible that such a "super"(deducing class method declaration
context) could appear in Python?
(It seems to me that to implement a simple super something should be
done during "compilation" of class declaration.)

Best regards,
Kerim mailto:[email protected]
 
K

Kristian Ovaska

Kerim Borchaev said:
Is it possible that such a "super"(deducing class method declaration
context) could appear in Python?
(It seems to me that to implement a simple super something should be
done during "compilation" of class declaration.)

The document "Unifying types and classes in Python 2.2" by Guido
probably answers your question.

Quote from http://www.python.org/2.2.3/descrintro.html#cooperation
regarding super:

"It would be nice if we didn't have to name the class explicitly, but
this would require more help from Python's parser than we can
currently get. I hope to fix this in a future Python release by making
the parser recognize super."
 
G

Gonçalo Rodrigues

Hello!

Always when I use "super" I create a code duplication because class
used as first arg to "super" is always the class where the method
containing "super" was defined in:
'''
class C:
def method(self):
super(C, self).method()
'''

Obviously the methods like the one below doesn't work "right";-)
'''
def super(self):
super(self.__class__, self)
class C:
def method(self):
super(self).method()
'''

Hmm... I once used super where the class used as first arg was *not*
the class defining the method, it was it's super class. So I was
calling, not the super method, but it's grand-super method.

Arguably, this is a clear sign of bad design on my part. But that's
how it went, anyway.

With my best regards,
G. Rodrigues
 
G

Gerrit Holl

Kristian said:
The document "Unifying types and classes in Python 2.2" by Guido
probably answers your question.

Quote from http://www.python.org/2.2.3/descrintro.html#cooperation
regarding super:

"It would be nice if we didn't have to name the class explicitly, but
this would require more help from Python's parser than we can
currently get. I hope to fix this in a future Python release by making
the parser recognize super."

Another quote from the same page :) :

--- start quote ---
Our second example creates a class, 'autosuper', which will add a
private class variable named __super, set to the value super(cls).
(Recall the discussion of self.__super above.) Now, __super is a private
name (starts with double underscore) but we want it to be a private name
of the class to be created, not a private name of autosuper. Thus, we
must do the name mangling ourselves, and use setattr() to set the class
variable. For the purpose of this example, I'm simplifying the name
mangling to "prepend an underscore and the class name". Again, it's
sufficient to override __init__ to do what we want, and again, we call
the base class __init__ cooperatively.

class autosuper(type):
def __init__(cls, name, bases, dict):
super(autosuper, cls).__init__(name, bases, dict)
setattr(cls, "_%s__super" % name, super(cls))

Now let's test autosuper with the classic diamond diagram:

class A:
__metaclass__ = autosuper
def meth(self):
return "A"
class B(A):
def meth(self):
return "B" + self.__super.meth()
class C(A):
def meth(self):
return "C" + self.__super.meth()
class D(C, B):
def meth(self):
return "D" + self.__super.meth()

assert D().meth() == "DCBA"

(Our autosuper metaclass is easily fooled if you define a subclass with
the same name as a base class; it should really check for that condition
and raise an error if it occurs. But that's more code than feels right
for an example, so I'll leave it as an exercise for the reader.)

--- end quote ---

yours,
Gerrit.
 
M

Michele Simionato

Kerim Borchaev said:
Hello!

Always when I use "super" I create a code duplication because class
used as first arg to "super" is always the class where the method
containing "super" was defined in:
'''
class C:
def method(self):
super(C, self).method()
'''

Obviously the methods like the one below doesn't work "right";-)
'''
def super(self):
super(self.__class__, self)
class C:
def method(self):
super(self).method()
'''

Is it possible that such a "super"(deducing class method declaration
context) could appear in Python?
(It seems to me that to implement a simple super something should be
done during "compilation" of class declaration.)

Best regards,
Kerim mailto:[email protected]

``super`` is one of the trickiest Python constructs. In
http://www.python.org/2.2.3/descrintro.html Guido sketches
a metaclass solution which however is not quite satisfactory (for one,
it does not work with magic methods).

Some time ago I went to "fix" autosuper, but that required a major metaclass
hacking which was so deep that I have already forgotten what I did ;)
Nevertheless, I have still the files around, and my test suite runs okay
(this only means that the bugs are smarter than me) so I think I will post
the code. If somebody uses it and finds an unexpected behavior, please
send me a note. If somebody wants his head to explode, please try to
understand what safetype does ;)

Here are two examples of usage:

# example1.py: the diamond diagram

from super import autosuper

class A(object):
__metaclass__=autosuper
def m(self):
return "A"
class B(A):
def m(self):
return "B" + self.__super.m()
class C(A):
def m(self):
return "C" + self.__super.m()
class D(C, B):
def m(self):
return "D" + self.__super.m()

print D().m()

this prints DCBA.

#example2.py

from super import autosuper

class A(str):
__metaclass__=autosuper
def __new__(cls):
obj='A'+cls.__super.__new__(cls)
print obj
return obj
class B(A):
def __new__(cls):
obj="B" + cls.__super.__new__(cls)
print obj
return obj
class C(A):
def __new__(cls):
obj="C" + cls.__super.__new__(cls)
print obj
return obj
class D(C, B):
def __new__(cls):
obj="D" + cls.__super.__new__(cls)
print obj
return obj

D()

this prints

A
BA
CBA
DCBA

Here is the module super.py:

# super.py

from safetype import safetype # deep magic to avoid metaclass conflicts

class _super(object):
"""Helper descriptor, called by the ``Enable__super`` metaclass which will
take care of defining the ``__thisclass__`` attribute; it should not be
called directly, unless you really know what you are doing. Notice that
this ``_super`` is minimal, i.e. it does not define ``__new__``,
`` __init__`` or other special methods; this avoids the problems of the
standard ``super``."""
def __get__(self,obj,klass):
if obj is None: obj=klass
return super(self.__thisclass__,obj)

class autosuper(safetype):
"""Cooperative safe metaclass which defines a private attribute ``__super``
on its instances, containing a reference to the descriptor ``_super``.
This enable the cooperative syntax ``obj.__super.methodname`` as
sugar for ``super(callingclass,obj).methodname``."""
def __init__(cls,*args):
super(autosuper,cls).__init__(*args)
if len(args)==1 or args[0]=='superobject': return # do nothing
strippedname=args[0].lstrip('_')
# if the class name starts with underscores, they must
# be stripped; this is how the mangling mechanism works
sup=_super(); sup.__thisclass__=cls # trick to avoid __init__ in _super
setattr(cls,'_%s__super' % strippedname,sup)

Here is the module safetype.py:

# safetype.py

"""Deep, **DEEP** magic to remove metaclass conflicts.

``safetype`` provides the ``safetype`` metaclass, the mother of conflict-free
metaclasses. The suggested import syntax for usage in other modules is

from safetype import safetype as type

If you override ``__new__`` when you derive from ``safetype``,
you should do it cooperatively.

Example:
.... def __new__(mcl,*args):
.... print 'creating a class from M'
.... return super(M,mcl).__new__(mcl,*args)
.... def __new__(mcl,*args):
.... print 'creating a class from N'
.... return super(N,mcl).__new__(mcl,*args)
.... __metaclass__=M
creating a class from M
.... __metaclass__=N
creating a class from N
.... pass
creating a class from M
creating a class from N
<class 'M'>
"""

import sys,sets,types,__builtin__

__type__=__builtin__.type
#the aboriginal 'type'; useful if you rebinds 'type' to 'safetype'

metadic={} # associates tuple of bases metaclasses to children metaclasses

class safetype(type):
"""Overrides the ``__new__`` method of the ``type`` metaclass, making the
generation of classes conflict-proof."""
# Seventeen lines of DENSE code!
def __new__(mcl,*args):
nargs=len(args)
if nargs==1: # works as __builtin__.type
return __type__(args[0])
elif nargs==3: # creates the class using the appropriate metaclass
n,b,d = args # name, bases and dictionary
mb=map(__type__,b) # metaclasses of the bases
meta=generatemetaclass([mcl,]+mb) # recursive
if mcl is meta: # meta is trivial, dispatch to the default __new__
return super(safetype,mcl).__new__(mcl,n,b,d)
elif is_less_specific(mcl,mb): # dispatch to meta.__new__
return meta.__new__(meta,n,b,d)
else: # non-trivial metaclass, dispatch to the right __new__
# (it will take a second round)
return super(mcl,meta).__new__(meta,n,b,d)
else:
raise TypeError('%s() takes 1 or 3 arguments' % mcl.__name__)

def generatemetaclass(metas):
"""Given a sequence of metaclasses, removes redundances and, if needed,
creates a new metaclass; returns the metaclass and updates the global
dictionary.of metaclasses. If the metaclass is already in the dictionary,
simply retrieves it."""

metabases=remove_redundant(metas)# metas have the priority
if metabases in metadic: # already generated metaclass
return metadic[metabases]
elif len(metabases)==1: # single metabase
meta=metabases[0]
else: # multiple metabases
metaname=''.join([m.__name__ for m in metabases])
meta=safetype(metaname,metabases,{})
return metadic.setdefault(metabases,meta)

def is_less_specific(c,ls):
"c is an ancestor of (at least) one class in the list ls."
for C in ls:
if issubclass(C,c) and C is not c: return True
return False

def remove_redundant(bases):
"""Returns a tuple of non-redundant base classes.
Given a sequence of base classes, a class is redundant if

1. it is duplicated;
2. it is implied by the others, i.e. it is an ancestor of at least one
of the other classes;
3. it is ClassType, the metaclass of old style classes.

For instance, if ``C`` is derived from ``B``, in the
sequence ``C,B`` the class ``B`` is redundant, since all its features are
already provided by ``C``. Therefore ``B``
is removed and ``remove_redundant`` returns the tuple ``(C,)``:
>>> class B(object): pass ...
>>> class C(B): pass ...
>>> import safetype; safetype.remove_redundant([C,B])
(<class 'C'>,)
"""
redundant=sets.Set((types.ClassType,)) # old style metaclass
ls=list(bases)
for c in bases:
if is_less_specific(c,ls) or c in redundant:
ls.remove(c)
else: # c is a redundant class to be removed if found
redundant.add(c)
return tuple(ls)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

super() in class defs? 4
Super() function 1
'super' object has no attribute '__setitem__' 2
super() woes (n00b) 12
using super 16
can someone explain 'super' to me? 2
super behavior 1
super, decorators and gettattribute 19

Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top