Is this a super bug?
Michele Simionato
mis6 at pitt.edu
Mon Apr 21 10:32:37 EDT 2003
"Bjorn Pettersen" <BPettersen at NAREX.com> wrote in message news:<mailman.1050791852.9118.python-list at python.org>...
> M:\python>python
> Python 2.3a2 (#39, Feb 19 2003, 17:58:58) [MSC v.1200 32 bit (Intel)] on
> win32
> Type "help", "copyright", "credits" or "license" for more information.
> >>> class ff(str):
> ... def foo(self):
> ... print 'str. getitem ', str. getitem (self, 0)
> ... print 'super(ff, self). getitem ', super(ff,
> self). getitem (0)
> ... print 'super(ff, self)[0]', super(ff, self)[0]
> ...
> >>> f = ff('asdf')
> >>> f.foo()
> str. getitem a
> super(ff, self). getitem a
> super(ff, self)[0]
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 5, in foo
> TypeError: unsubscriptable object
>
This is not a bug, since super(cls,self) is supposed to return a super
object, and not self. Nevertheless super has various bugs: it cannot retrieve
properties, cannot retrieve __name__, does not work always properly
in multiple inheritance of a class and a metaclass, etc. If you google a bit,
you will find various posts of mine about problems with super.
At the end I got really tired, started studying attribute descriptors
(completely undocumented, AFAIK), spent a lot of time and come with the
following code.
Warning: this is quite non-trivial code and I don't claim I have tested it
in any possible situation.
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")
class convert2descriptor(object): # used in _super
"""To all practical means, this class acts as a function that, given an
object, adds to it a __get__ method if it is not already there. The
added __get__ method is trivial and simply returns the original object,
independently from obj and cls."""
def __new__(cls,a):
if hasattr(a,"__get__"): # do nothing
return a # a is already a descriptor
else: # creates a trivial attribute descriptor
cls.a=a
return object.__new__(cls,a)
def __get__(self,obj,cls=None):
"Returns self.a independently from obj and cls"
return self.a
def _super(C,S,methname):
"""Internal function invoking ancestor. Returns an attribute descriptor
object."""
if methname=='__name__': # special case
meth=ancestor(C,S)[1].__name__
else:
for c in ancestor(C,S)[1:]:
meth=c.__dict__.get(methname)
if meth: break # if found
if not meth: raise AttributeError,methname # not found
return convert2descriptor(meth) # if needed
class Super(object):
"""Invoked as Super(cls,obj).meth returns the supermethod of
cls with respect to the MRO of obj. If obj is a subclass of cls,
it returns the unbound supermethod of obj; otherwise, if obj is an
instance of some subclass of cls, it returns the obj-bound method."""
def __init__(self,cls,obj):
self.cls=cls
self.obj=obj
def __getattribute__(self,name): # how to redefine __getattribute__ properly
obj=object.__getattribute__(self,'obj')
cls=object.__getattribute__(self,'cls')
if hasattr(obj,'__bases__') and issubclass(obj,cls):
# if obj is a subclass, return unbound method
return _super(cls,obj,name).__get__(None,obj)
else: # if obj is an instance, return bound method
S=type(obj)
return _super(cls,S,name).__get__(obj,S)
# few checks
class ExampleBaseClass(object):
"""Contains a regular method 'm', a staticmethod 's', a classmethod
'c', a property 'p' and a data attribute 'd'."""
m=lambda self: 'regular method'
s=staticmethod(lambda : 'staticmethod')
c=classmethod(lambda cls: 'classmethod')
p=property(lambda self: 'property')
d='data'
class C(ExampleBaseClass): pass
c=C()
print Super(C,C).p #<property object>
print Super(C,c).p #property
print Super(C,c).__name__ # ExampleBaseClasss
print Super(C,C).__name__ # ExampleBaseClass
More information about the Python-list
mailing list