views:

97

answers:

2

I have a bit of Python code which depends on type checking. I'll try and phrase my problem in the language of math so it's clear. I have a few classes which correspond to subsets of each other and form an inheritance chain.

class Real(object):
    pass

class Integer(Real):
    pass

class Natural(Integer):
    pass

And I have a tuples containing types. Each of these corresponds to the domain of some function.

t1 = ( Real, Real )
t2 = ( Real , Integer )

I would like to do some form of type checking such that given a another tuple ( Natural , Natural ) if every coordinate in the tuple is a subclass of the specified domains. For example for some function getcompatibles I'd like to have:

getcompatibles( ( Real, Real ) ) = [ t1 ]
getcompatibles( ( Real, Integer ) ) = [ t1, t2 ]
getcompatibles( ( Natural, Natural ) ) = [ t1, t2 ]
getcompatibles( ( Natural, Real ) ) = [ t1 ]

The only solution I could come up with would be to run through every for domain (t1, t2) run through each of the types in __subclasses__ and check to see whether it isinstance is True for the given input.

Thats extremely inefficient though, is there perhaps a more Pythonic way of doing this?

+3  A: 
def compatible_pred(obj_types, fun_signature):
  if len(obj_types) != len(fun_signature): return False
  return all(issubclass(of, ft) for of, ft in zip(obj_types, fun_signature))

def is_compatible(obj_types, fun_signatures=(t1, t2)):
  return [t for t in fun_signatures if compatible_pred(obj_types, t)]

The is_compatible name for something that is not a predicate is really, truly confusing: why not give it a sensible name such as getcompatibles, so that the strongly predicate-sounding iscompatible could be used instead for what I've had to name compatible_pred?

Alex Martelli
Works beautifully, thanks!
freyrs
@Mark, fixed, thanks.
Alex Martelli
@freyrs, you're welcome -- always glad to help!
Alex Martelli
A: 

Don't check types when you don't have to and count on exception handling - with try / except to catch the instances where the expectation is violated.

In Python, which is a "lazy typed" (but strongly typed, less I upset the purists) language, calling isinstance repeatedly will certainly cost you in overhead. The question I ask myself when facing such design questions is "If you didn't want this function to handle pairs of Naturals, why did you call it with them?" Presumably you are doing some sort of conditional branch predicated on is_compatible, I'd suggest you change that into a conditional call.

Seeing how you intend to use the result of is_compatible would allow a more focused answer.

msw