简体   繁体   English

如何为函数实现__str__?

[英]How do you implement __str__ for a function?

Given a function foo : 给定函数foo

def foo(x):
     pass

Printing its representation by invoking str or repr gives you something boring like this: 通过调用strrepr打印它的表示形式会给你一些无聊的东西:

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. 我想知道是否可以覆盖函数的__str__方法来打印其他东西。 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. 现在,我明白函数的描述应该来自__doc__ ,这是函数的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? 在试图找出这个问题的解决方案时,我遇到了为classes实现__str__如何classes 定义__str__方法?

This approach involved defining a metaclass with an __str__ method, and then attempting to assign the __metaclass__ hook in the actual class. 这种方法涉及使用__str__方法定义元类,然后尝试在实际类中分配__metaclass__钩子。

I wondered whether the same could be done to the class function , so here's what I tried - 我想知道是否可以对类function做同样的事情,所以这就是我试过的 -

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? 那么,为函数实现__str__的最佳方法是什么?

A function in Python is just a callable object. Python中的函数只是一个可调用的对象。 Using def to define function is one way to create such an object. 使用def来定义函数是创建这样一个对象的一种方法。 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__ . 除了最后一个显然允许我们设置函数类型的特殊方法,如__str____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. 但是为此创建一个类型变得有点单调乏味,这也使得理解函数的功能变得更加困难:毕竟, def语法允许我们定义函数体。 So we want to keep it that way! 所以我们想保持这种方式!

Luckily, Python has this great feature called decorators which we can use here. 幸运的是,Python有一个很棒的功能叫做装饰器,我们可以在这里使用它。 We can create a function decorator that will wrap any function inside a custom type which calls a custom function for the __str__ . 我们可以创建一个函数装饰器,它将自定义类型中的任何函数包装起来,调用__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. 然后我们可以通过简单地装饰它来将__str__函数添加到任何函数中。 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. 显然,这样做有一些限制和缺点,因为你不能完全重现def对装饰器内的新函数的作用。

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 . 例如,使用inspect模块查看参数将无法正常工作:对于可调用类型,它将包含self参数,并且在使用泛型装饰器时,它只能报告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. 但这通常意味着您需要投入大量精力才能在函数对象上获得__str__ ,这可能很少被使用。 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. 因此,您应该考虑是否确实需要为函数实现__str__ ,以及您将对这些函数执行何种操作。

If you find yourself wrapping functions, it's useful to look at functools.partial . 如果你发现自己包装函数,那么查看functools.partial很有用。 It's primarily for binding arguments of course, but that's optional. 它当然主要用于绑定参数,但这是可选的。 It's also a class that wraps functions, removing the boilerplate of doing so from scratch. 它也是一个包装函数的类,从头开始删除这样做的样板。

from functools import partial

class foo(partial):
    def __str__(self):
        return "I'm foo!"

@foo
def foo():
    pass

assert foo() is None
assert str(foo) == "I'm foo!"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM