如何为函数实现__str__?

cs9*_*s95 26 python function

给定一个功能foo:

def foo(x):
     pass
Run Code Online (Sandbox Code Playgroud)

通过调用strrepr给你一些像这样无聊的东西打印它的表示:

str(foo)
'<function foo at 0x119e0c8c8>'
Run Code Online (Sandbox Code Playgroud)

我想知道是否可以覆盖函数的__str__方法来打印其他东西.基本上,我想做:

str(foo)
"I'm foo!'
Run Code Online (Sandbox Code Playgroud)

现在,我明白函数的描述应该来自__doc__函数的docstring.然而,这仅仅是一个实验.

在试图找出一个解决这个问题,我碰到来实现__str__classes:如何定义一个类__str__方法?

这种方法涉及使用__str__方法定义元类,然后尝试__metaclass__在实际类中分配钩子.

我想知道是否可以对班级做同样的事情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)
Run Code Online (Sandbox Code Playgroud)

我认为它不会起作用,但值得一试!

那么,实现__str__函数的最佳方法是什么?

pok*_*oke 29

Python中的函数只是一个可调用的对象.使用def自定义函数是一个创建这样一个对象的方式.但实际上没有什么可以阻止你创建一个可调用类型并创建它的实例来获得一个函数.

所以以下两件事基本相同:

def foo ():
    print('hello world')


class FooFunction:
    def __call__ (self):
        print('hello world')

foo = FooFunction()
Run Code Online (Sandbox Code Playgroud)

除了最后一个显然允许我们设置函数类型的特殊方法,比如__str____repr__.

class FooFunction:
    def __call__ (self):
        print('hello world')

    def __str__ (self):
        return 'Foo function'

foo = FooFunction()
print(foo) # Foo function
Run Code Online (Sandbox Code Playgroud)

但是为此创建一个类型变得有点单调乏味,它也使得理解函数的功能变得更加困难:毕竟,def语法允许我们定义函数体.所以我们想保持这种方式!

幸运的是,Python有一个很棒的功能叫做装饰器,我们可以在这里使用它.我们可以创建一个函数装饰器,它将自定义类型中的任何函数包装起来,调用自定义函数__str__.这看起来像这样:

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
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用它__str__通过简单地装饰它来为任何函数添加函数.这看起来像这样:

def foo_str ():
    return 'This is the __str__ for the foo function'

@with_str(foo_str)
def foo ():
    print('hello world')
Run Code Online (Sandbox Code Playgroud)
>>> str(foo)
'This is the __str__ for the foo function'
>>> foo()
hello world
Run Code Online (Sandbox Code Playgroud)

显然,这样做有一些限制和缺点,因为你不能完全重现def那个装饰器内的新功能.

例如,使用inspect模块查看参数将无法正常工作:对于可调用类型,它将包含self参数,并且在使用通用装饰器时,它只能报告详细信息wrapper.但是,可能存在一些解决方案,例如在此问题中讨论的,将允许您恢复某些功能.

但这通常意味着您需要投入大量精力才能获得__str__可能很少使用的函数对象.因此,您应该考虑是否确实需要__str__实现函数,以及您将对这些函数执行何种操作.

  • 当然,除了函数调用功能之外的所有内容都会在执行此操作时中断.其中一些可以手动处理,如描述符协议.其中一些可以处理,如签名内省.其中一些永远不会起作用. (5认同)

A. *_*ady 10

如果你发现自己包装函数,那么查看它会很有用functools.partial.它当然主要用于绑定参数,但这是可选的.它也是一个包装函数的类,从头开始删除这样做的样板.

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!"
Run Code Online (Sandbox Code Playgroud)