何时使用 __call__ 是个好主意?
大家对使用 __call__
有什么看法呢?我很少见到它被用到,但我觉得它是个很方便的工具,特别是当你知道一个类会被用来实现一些默认行为的时候。
4 个回答
0
通常情况下,当一个类被当作函数使用,并且有一些实例上下文时,就会用到这个概念。比如说,有一个叫做@DecoratorClass('some param')
的装饰器类,这里的'some param'会被存储在这个实例的命名空间中,然后这个实例就可以作为实际的装饰器被调用。
但是,如果你的类提供了不同的方法,这个用法就不是很有用了,因为这样调用的效果通常不太明显。在这种情况下,明确的写法比模糊的写法要好。
3
如果你需要让你的对象可以被调用,就用这个,它就是为了这个目的而存在的。
我不太明白你说的默认行为是什么意思。
我发现它特别有用的一个地方是,当我使用一个包装器或者类似的东西时,这个对象是在某个框架或库的深处被调用的。
45
我觉得你的直觉是对的。
从历史上看,可调用对象(有时我听到叫“函数对象”)在面向对象编程中被用来模拟闭包。在C++中,它们常常是必不可少的。
不过,在Python中,__call__
面临着不少竞争:
- 一个普通的命名方法,它的行为有时从名字上就能很容易推测出来。可以转换为绑定方法,这样就可以像函数一样调用。
- 闭包,通过返回在嵌套块中定义的函数来获得。
- lambda表达式,这是一种有限但快速创建闭包的方法。
- 生成器和协程,它们的主体可以像函数对象一样保存状态。
我认为使用__call__
的时机是当你不适合使用上述选项中的任何一个时。可以检查以下标准:
- 你的对象有状态。
- 你的类有一个明显的“主要”行为,但给它命名有点傻。例如,如果你发现自己在写
run()
、doStuff()
、go()
或者常见的冗余的doRun()
,那么你可能有一个合适的候选者。 - 你的对象的状态超出了生成器函数的预期。
- 你的对象封装、模拟或抽象了函数的概念。
- 你的对象还有其他辅助方法,这些方法在概念上与主要行为相关。
我喜欢的一个例子是UI命令对象。它们的主要任务是执行命令,但还带有额外的方法来控制它们在菜单项中的显示,这种情况下我觉得使用可调用对象是很合适的。