Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you implement __str__ for a function?

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?

like image 212
cs95 Avatar asked Nov 23 '17 09:11

cs95


People also ask

What is __ str __ function in Python?

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.

What is the purpose of __ Str__ method in Django?

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.

What is the purpose of defining the functions __ str __ and __ repr __ within a class how are the two functions different?

__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.

What is def __ str __( self in Django?

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.


1 Answers

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.

like image 145
poke Avatar answered Oct 04 '22 11:10

poke