2

I think is is best explained with an example. Suppose I have a method that calculates the distances between two vectors and prints it. I also want that method to print the distance measure that was used. The distance measure is given to the function by the caller in the form of a callable object. If the callable is an instance of some class, I can provide the __str__ method to make it print out the name of the distance measure. But the callable can also be a function, and I have not found a way to change __str__ in that case. Some code:

def distance(v1, v2, d):
    print d
    dist = d(v1, v2)
    print dist
    return dist

If d is a function, print d will print out something like <function someFunc at 0x1b68830>. How can I change this? Just printing out the name of the function would be fine, since I usually give them readable names.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283

4 Answers4

2

I don't think functions can be subclassed, which is what you'd need to do in order to change a function's __str__ method. It's much easier to make a class behave like functions (using the __call__ method).

Functions have a func_name attribute, that returns the function's name.

If you choose to use the func_name attribute, then your callable objects would need a func_name attribute too.

Since you've already defined the class's __str__ method, you could make func_name a property to return str(self) like this:

def dfunc(v1,v2):
    return 1

class FooDist(object):
    def __call__(self,v1,v2):
        return 1
    @property
    def func_name(self):
        return str(self)
    def __str__(self):
        return 'My FooDist'


def distance(v1, v2, d):
    print d.func_name
    dist = d(v1, v2)
    print dist
    return dist

distance(1,2,dfunc)
# dfunc
distance(1,2,FooDist())
# My FooDist
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
1

You can do

import types
if isinstance(d, types.FunctionType):
    print "<function %s>" % d.__name__
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
1

To get a function's name, see this question. To get a class's name, see this question. Putting them together:

def distance(v1, v2, d):
    if hasattr(d, '__name__'):
        print d.__name__
    elif hasattr(d, '__class__'):
        print d.__class__.__name__
    else:
        print d   # unsure about this case
    dist = d(v1, v2)
    print dist
    return dist
Community
  • 1
  • 1
snapshoe
  • 13,454
  • 1
  • 24
  • 28
0

You can uniformly give both classes and functions your own attributes:

class MyDistanceMeasure:
    name = "mine"
    #...

def another_distance_measure(x,y):
    #...

another_distance_measure.name = "another"

then:

def distance(v1, v2, d):
    print d.name
    # or..
    print getattr(d, 'name', "unknown")

    dist = d(v1, v2)
    print dist
    return dist
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662