9

I want to change the string representation of a python function to be just the function name.

Eg for some function

def blah(x):
 ...

str(blah) currently gives

<function blah at 0x10127b578>

So I replace __str__ like this:

blah.__str__=lambda: 'blah'

but it doesn't get called by str(blah).

Is it possible to change __str__ for a function?

macduff
  • 1,583
  • 1
  • 9
  • 9
  • 3
    use a class instead ... – Joran Beasley Apr 01 '14 at 21:54
  • possible duplicate of http://stackoverflow.com/questions/10493025/why-cant-i-replace-the-str-method-of-a-python-object-with-another-function since you're trying to override an instance of the `function` class's `.__str__` argument, the latter which is only designed to be replaced in the class definition. – Adam Smith Apr 01 '14 at 21:57
  • just a minor aside, In general I would avoid this kind of thing as it makes it hard for other people who may have to work on your code. while its nice and convient, typically explicit is better than implicit – Joran Beasley Apr 01 '14 at 22:45

4 Answers4

8

Replacing __str__ like that never actually works on any type of object. The builtin function type isn't special:

>>> class Foo(object):
    pass

>>> f = Foo()
>>> f.__str__ = lambda: 'f'
>>> str(f)
'<__main__.Foo object at 0x0000000002D13F28>'
>>> f.__str__()
'f'    

The str builtin doesn't lookup __str__ via the usual lookup protocol, it skips straight to looking at the class of its argument. So you can't "override" the __str__ method on an individual object; you have to set it on a class to apply to all objects of that class.1

But then again, the builtin function type isn't special. You can make a class that behaves mostly like a function, and use that instead:

class Function(object):
    def __init__(self, raw_function):
        self.raw_function = raw_function
    def __call__(self, *args, **kwargs):
        return self.raw_function(*args, **kwargs)
    def __str__(self):
        return self.raw_function.__name__

The __call__ method means you can call objects of this class exactly as if they were functions. This one just passes whatever arguments it receives straight through to the underlying raw function and returns whatever it returns (or throws whatever exceptions it throws). And since this class takes a single function as an argument, it also fits the pattern for a decorator:

@Function
def blah(x):
    return x + 1

With that:

>>> blah
<__main__.Function object at 0x0000000002D13EB8>
>>> str(blah)
'blah'
>>> blah(33)
34

1 The builtin function type is special in some regards (I lied), one of which being that you can't assign to its attributes (that's the attributes of the class itself, not the attributes of any particular function object). So before you think maybe it would be great if you could monkeypatch the builtin function type so that str worked the way you want on all functions, that doesn't work. It's probably for the best; modifying things as global and fundamental as the builtin function type would be a great way to invent some really bizarre bugs.

Ben
  • 68,572
  • 20
  • 126
  • 174
6

As Joran said:

class NamedFunction:
    def __init__(self, name, f):
        self.f = f
        self.name = name

    def __call__(self, *args, **kwargs):
        return self.f(*args, **kwargs)

    def __str__(self):
        return self.name


f = NamedFunction("lambda: 'blah'", lambda: 'blah')
print(f())
print(f)
Peter Varo
  • 11,726
  • 7
  • 55
  • 77
Hyperboreus
  • 31,997
  • 9
  • 47
  • 87
4
class FNMagic:
     def __init__(self,fn,fn_name):
         self.fn = fn 
         self.fn_name = fn_name
     def __call__(self,*args,**kwargs):
         return self.fn(*args,**kwargs)
     def __str__(self):
         return self.fn_name


def blah(x):
    return x

blah = FNMagic(blah,"blah!")
print blah

you could make a simple decorator

class NamedFn:
    def __init__(self,name):
       self.fn_name = name
    def __call__(self,fn):
        return FNMagic(fn,self.fn_name)

@NamedFn("some_name!!!")
def blah2(x,y):
     return x*y

print blah2
Joran Beasley
  • 110,522
  • 12
  • 160
  • 179
  • I'm a little light on experience with decorators, but this seems like a good use for one. Maybe add a little code about it? – Adam Smith Apr 01 '14 at 22:00
  • 1
    theres a simple decorator ... (of coarse the FnMagic class could have been declared inside the decorator but that would work fine) – Joran Beasley Apr 01 '14 at 22:06
3

As noted already, the answer is no. If you just want to get the name of a function as a string, you can use blah.__name__.

khagler
  • 3,996
  • 29
  • 40