views:

84

answers:

3

I want to know if a function is defined for an instance of a class

class c(object):
  def func(self): pass
cc = c()

>>> is_function_defined_for_instance(cc,'func')
True
>>> is_function_defined_for_instance(cc,'cnuf')
False

my cleanest attempts at the function is:

def is_function_defined_for_instance(instance,function):
  return callable(getattr(instance,function,None))

# returns True iff function is defined for class of instance (not super classes)
def is_function_defined_for_instance(instance,function):
  return bool(instance.__class__.__dict__.get(function))

is there a built-in or a nicer way to accomplish what I'm looking for?

A: 

Can you throw some sort of custom exception if the method is missing? (I'm coming from Ruby land here :) )

actually, it is probably the right way to do things most of the time (easier to ask forgiveness)
cobbal
+2  A: 
>>> def is_func(instance, func):
    test = getattr(instance, func, None)
    if test and hasattr(test, '__call__'):
        return True

>>> is_func('', 'len')
>>> is_func({}, 'fromkeys')
True

or you could use inspect module:

>>> import inspect
>>> test = getattr(cc, 'func', None)
>>> inspect.ismethod(test)
True
SilentGhost
what's the difference between callable(function) and hasattr(function,'__call__') ?
random guy
`callable` doesn't exist in py3k
SilentGhost
+2  A: 

In Python 2.6 or better, the recommended approach is:

import collections

def is_func(instance, func):
  return isinstance(getattr(instance, func, None), collections.Callable)

The new ABCs (Abstract Base Classes) in the collections module are the correct way, in Python 2.6+ and 3.any, to perform this kind of "duck-typing-like type checks" -- much more elegant, unform, and future-proof than older approaches such as the callable built-in, operator.isCallable, or checking for the presence of specific special methods such as __call__. Since isinstance can now be overloaded, leave it up to the ABCs to overload it and perform the checks by encapsulating whatever lower-level approaches work best!

The elegance and advantages of the new approach are such that, if you need to make your code portable between older and newer versions of Python, I recommend encapsulating the "duck-typing tests" that you require in a module that can define its function depending on the sys.version -- using isinstance(x, collections.Y) in 2.6 or better, and older approaches such as hasattr(x, '__somespecialmethod__') or callable(x) in 2.5 or worse.

Most importantly, and more generally, don't pollute your new, mainline application code with the old, inferior ways -- if backwards-portability requirements force your overall system to have some "garbage" (in order to keep running on old Python versions), at least hide that garbage decently in its own "compartment" (where one day you may be able to easily sweep it away if your app's deployment constraints permit!-).

Alex Martelli