2007-05-20
Properties relevant to finding what class supplies a method
For my own future reference if nothing else, here's some properties relevant to finding what superclass supplies a method:
On super() objects, we have:
__self__ |
the object you are calling super() about,
ie the second argument. |
__self_class__ |
the actual class of said object; equivalent
to .__self__.__class__ but more convenient. |
__this_class__ |
the class that super() was invoked for,
ie its first argument; this is where you are right now in the
superclass hierarchy. |
(Because the MRO varies by class, you need both the real class of the object and the current class.)
Unbound methods from Python classes show up as type 'unbound method'
in repr(), and have an im_class attribute that points to their
class (and an im_func attribute that points to the actual Python
function).
Some methods from builtin classes, such as object.__init__, show up
as type 'slot wrapper'. These have an __objclass__ attribute that
points to their class.
Other methods from builtin classes, such as object.__new__, show
up as type 'built-in method'. These have a __self__ attribute
that points to their class.
Fortunately you can tell the three types of objects apart based on what attribute they have, because nothing has more than one of them (so far; this is where I start to think that this is fishing in the implementation defined waters).
2007-05-17
Determining what superclass supplies a method in Python
It turns out that there are some situations in Python where it is useful to
know what your superclass is. In particular, it
would be nice for mixin classes to be able to know when they are about
to call methods on object() instead of on some actual user class.
It would be nice if the objects returned by super() directly exposed a
way of determining this. Unfortunately they do not, and they don't even
directly expose what the next superclass is, although given a super
object you can recover the information. The code to do it is even
reasonably short:
def getsuper(kls, rkls):
mro = rkls.__mro__
i = list(mro).index(kls)
return mro[i+1]
def getprovider(so, meth):
s = getsuper(so.__thisclass__,
so.__self_class__)
r = getattr(s, meth)
if hasattr(r, "im_class"):
return r.im_class
elif hasattr(r, "__objclass__"):
return r.__objclass__
else:
return None
The getprovider function is called with the object you get from
super() and the name of the method you want to look up (the method
is assumed to exist somewhere). You get back the class that provides
it, provided that I've found all of the little twisty special cases,
and provided that we can actually tell. Fortunately we can tell for
__init__, although we cannot always tell for other methods.
(Python is sometimes irritatingly inconsistent about this sort of
introspection, so I am probably still missing some special cases.
I believe that any return of None means you are dealing with a
built-in class or method.)