tags:

views:

118

answers:

4

I've got a registry of classes and types in Python 2.5, like so:

class ClassA(object):
    pass

class ClassB(ClassA):
    pass 

MY_TYPES = {
    basestring : 'A string',
    int : 'An integer',
    ClassA : 'This is ClassA or a subclass',
}

I'd like to be able to pass types to a function, and have it look up the closest matching type in the hierarchy. So, looking up str would return "A string" and looking up ClassB would return "This is ClassA or a subclass" The problem is, I don't know how to find the superclass (or, rather, trace the MRO chain) of a type object.

What's the best way of handling this?

+2  A: 

To get the superclasses of a class, use __bases__:

class ClassA(object):
    pass

class ClassB(ClassA):
    pass

print ClassB.__bases__
(<class '__main__.ClassA'>,)

Beware of things like int, though. The base for all of those will be <type 'object'>. You'll have to do some testing and iteration to match the dict keys you've listed in your example.

Another option would be to use isinstance on any object instance, or issubclass on the parameters, testing each against your dict keys. Some consider isinstance and its ilk to be the mark of the devil, but c'est la vie. Again, though, beware of using issubclass with ints and other such types. You'll probably need to combine a few approaches given your dict keys.

Jarret Hardie
+4  A: 
from inspect import getmro
[st for cls, st in MY_TYPES.items() if cls in getmro(ClassB)]

['This is ClassA or a subclass']

or if you're only interested in first match(es) generator version:

(st for cls, st in MY_TYPES.iteritems() if cls in getmro(ClassB))
vartec
nice! I always forget about inspect... must be a personality defect or something.
Jarret Hardie
A: 

This is really a job for generic functions. See PEAK-Rules and PEP 3124. Generic functions will allow you to dispatch arbitrary functions based on argument types, or even more complex expressions.

Or if you're really a sucker for punishment, witness this chapter from Practical Common Lisp.

zweiterlinde
+1  A: 

(st for cls, st in MY_TYPES.iteritems() if cls in getmro(ClassB))

The simpler way to write that would be:

(st for cls, st in MY_TYPES.iteritems() if issubclass(ClassB, cls))

However this does not find the “nearest” match; if ‘object’ were in the lookup, it could match everything! If you had things that were subclasses of other things in the lookup, or you wanted to support multiple inheritance, you'd have to pick out the one that was the first in MRO:

for cls in inspect.getmro(ClassB):
    if cls in MY_TYPES:
        print MY_TYPES[cls]
        break
else:
    print 'Dunno what it is'

Anyway... I would generally regard type lookups as a little bit of a code smell, is there no nicer way to do what you want?

bobince
I'm using a a widget library and a database library. I need to assign widgets to display particular database fields in a flexible way. Saying a "TextWidget" should be used to display a "basestring" is the best way I can see. And doesn't depend on a particular set of database types to be defined.
Chris B.