【问题标题】:How do you implement __str__ for a function?你如何为函数实现 __str__ ?
【发布时间】:2018-05-07 05:50:24
【问题描述】:

给定一个函数foo

def foo(x):
     pass

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

str(foo)
'<function foo at 0x119e0c8c8>'

我想知道是否可以覆盖函数的__str__ 方法来打印其他内容。本质上,我想做:

str(foo)
"I'm foo!'

现在,我知道函数的描述应该来自函数的文档字符串__doc__。然而,这只是一个实验。

在试图找出解决这个问题的方法时,我遇到了为 classes 实现 __str__How to define a __str__ method for a class?

这种方法涉及使用__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)

我认为这行不通,但值得一试!

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

【问题讨论】:

  • str(foo) 期间调用了哪些方法/函数?不应该是foo.__str__()吗?为什么我们不能简单地做foo.__str__ = lambda : "I'm a foo!"
  • @EricDuminil __str__ 函数不可写。
  • @MosesKoledoye:Python 在foo.__str__ = lambda : "I'm a foo!" 期间不会抱怨,因为它是私有方法,所以语法应该不同吗?作业被忽略了吗?
  • @EricDuminil 是的,它不会抱怨,因为任意属性可以绑定到函数实例。其实你可以做f.__str__ = lambda: 6foo.__str__()返回6;不强制执行字符串返回值。 __str__ 是通过 type(foo) 使用钩子调用的函数,而不是通过实例 foo
  • 太好了,谢谢。

标签: python function


【解决方案1】:

如果您发现自己在封装函数,请查看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!"

【讨论】:

    【解决方案2】:

    Python 中的函数只是一个可调用对象。使用def 定义函数是创建此类对象的一种 方法。但实际上没有什么能阻止您创建可调用类型并创建它的实例以获取函数。

    所以下面两件事基本相等:

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

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

    class FooFunction:
        def __call__ (self):
            print('hello world')
    
        def __str__ (self):
            return 'Foo function'
    
    foo = FooFunction()
    print(foo) # Foo function
    

    但是为此创建一个类型变得有点乏味,而且也让理解函数的作用变得更加困难:毕竟,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
    

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

    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
    

    显然,这样做有一些限制和缺点,因为您无法准确地重现 def 会为该装饰器中的新函数所做的事情。

    例如,使用inspect 模块查看参数将无法正常工作:对于可调用类型,它将包含self 参数,当使用泛型装饰器时,它将只能报告wrapper 的详细信息。但是,可能有一些解决方案,例如在this question 中讨论的,可以让您恢复一些的功能。

    但这通常意味着您投入大量精力只是为了在可能很少使用的函数对象上获得__str__ 工作。所以你应该考虑一下你的函数是否真的需要一个__str__ 实现,然后你会对这些函数做什么样的操作。

    【讨论】:

    • 当然,当你这样做时,除了函数调用功能之外的几乎所有东西都会中断。其中一些可以手动处理,例如描述符协议。其中一些可以处理,例如签名内省。其中一些永远不会起作用。
    • @PM2Ring 将答案更改为用functools.wrapped 包装最终函数,以便恢复基本要素:)
    • 您可以通过将 __call__ 设为静态方法 (@staticmethod def __call__(no, self, arg):) 来消除对 self 参数的需求。使用functools.wraps 恢复一些函数属性(如名称和注释)。否则,很好的答案!
    • @KenHilton @staticmethod 与忽略传递的 self 参数相同(Python 仍会将 self 传递给函数;只是装饰器会为您跳过它)。包装函数 again 并没有真正的好处。这里已经涉及到足够多的包装级别。另外,__call__ 仍然是一个实例方法,所以即使我在这里不需要self,将其设置为静态方法也会感觉不对。 – 至于wraps,我正在使用它。
    猜你喜欢
    • 1970-01-01
    • 2014-05-12
    • 1970-01-01
    • 2020-08-09
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多