Given a function foo
:
def foo(x): pass
Printing its representation by invoking str
or repr
gives you something boring like this:
str(foo) '<function foo at 0x119e0c8c8>'
I'd like to know if it is possible to override a function's __str__
method to print something else. Essentially, I'd like to do:
str(foo) "I'm foo!'
Now, I understand that the description of a function should come from __doc__
which is the function's docstring. However, this is merely an experiment.
In attempting to figure out a solution to this problem, I came across implementing __str__
for classes
: How to define a __str__ method for a class?
This approach involved defining a metaclass with an __str__
method, and then attempting to assign the __metaclass__
hook in the actual class.
I wondered whether the same could be done to the class function
, so here's what I tried -
In [355]: foo.__class__ Out[355]: function In [356]: class fancyfunction(type): ...: def __str__(self): ...: return self.__name__ ...: In [357]: foo.__class__.__metaclass__ = fancyfunction --------------------------------------------------------------------------- TypeError Traceback (most recent call last)
I figured it wouldn't work, but it was worth a shot!
So, what's the best way to implement __str__
for a function?
Python __str__() This method returns the string representation of the object. This method is called when print() or str() function is invoked on an object. This method must return the String object.
str function in a django model returns a string that is exactly rendered as the display name of instances for that model. # Create your models here. This will display the objects as something always in the admin interface.
__str__ is used in to show a string representation of your object to be read easily by others. __repr__ is used to show a string representation of the object.
def str(self): is a python method which is called when we use print/str to convert object into a string. It is predefined , however can be customised.
A function in Python is just a callable object. Using def
to define function is one way to create such an object. But there is actually nothing stopping you from creating a callable type and creating an instance of it to get a function.
So the following two things are basically equal:
def foo (): print('hello world') class FooFunction: def __call__ (self): print('hello world') foo = FooFunction()
Except that the last one obviously allows us to set the function type’s special methods, like __str__
and __repr__
.
class FooFunction: def __call__ (self): print('hello world') def __str__ (self): return 'Foo function' foo = FooFunction() print(foo) # Foo function
But creating a type just for this becomes a bit tedious and it also makes it more difficult to understand what the function does: After all, the def
syntax allows us to just define the function body. So we want to keep it that way!
Luckily, Python has this great feature called decorators which we can use here. We can create a function decorator that will wrap any function inside a custom type which calls a custom function for the __str__
. That could look like this:
def with_str (str_func): def wrapper (f): class FuncType: def __call__ (self, *args, **kwargs): # call the original function return f(*args, **kwargs) def __str__ (self): # call the custom __str__ function return str_func() # decorate with functool.wraps to make the resulting function appear like f return functools.wraps(f)(FuncType()) return wrapper
We can then use that to add a __str__
function to any function by simply decorating it. That would look like this:
def foo_str (): return 'This is the __str__ for the foo function' @with_str(foo_str) def foo (): print('hello world')
>>> str(foo) 'This is the __str__ for the foo function' >>> foo() hello world
Obviously, doing this has some limitations and drawbacks since you cannot exactly reproduce what def
would do for a new function inside that decorator.
For example, using the inspect
module to look at the arguments will not work properly: For the callable type, it will include the self
argument and when using the generic decorator, it will only be able to report the details of wrapper
. However, there might be some solutions, for example discussed in this question, that will allow you to restore some of the functionality.
But that usually means you are investing a lot of effort just to get a __str__
work on a function object which will probably very rarely be used. So you should think about whether you actually need a __str__
implementation for your functions, and what kind of operations you will do on those functions then.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With