在同一实例中使类可调用

23 投票
2 回答
29624 浏览
提问于 2025-04-17 14:04
class Foo(object):
    def tick(self):
        print("something")

class Bar(object):
    def __init__(self):
        self.foo = Foo()

    def tick(self):
        #Here's what I do....
        self.foo.tick()

        #here's what my goal would be
        self.foo()

b = Bar()
b.tick()

这基本上就是我的目标。从我了解到的情况来看,我可以把tick函数改成__call__,这样就能实现我想要的功能。有几个其他的回答提到,这样做会创建一个新的对象实例,这是不是意味着它会使用self.foo的内存?还是说会创建一个全新的对象,完全是新的实例?或者是self.foo的一个副本?

另外,我想到了一些可能会出现的缺点。对于我程序中的某个部分,我需要检查这个对象是否有__call__,以确定我传递的参数是一个函数还是一个变量。我其实不太想让它被调用(尽管我想,从技术上讲,这个类在那时确实会变成一个函数)。有没有什么办法可以区分一个函数和一个可调用的类呢?

还有其他什么原因会让这样做变得不太好(而且这样做算不算是符合Python风格呢)?我接下来想到的是,其他以__开头的变量不能在类外使用,但在这里似乎并不是这样。

2 个回答

22

要让一个类的实例可以像函数一样被调用,你只需要实现一个叫做 __call__ 的方法。然后,要调用这个 __call__ 方法,你只需这样做: instance(arg1,arg2,...) —— 换句话说,你可以像调用函数一样调用这个实例。

需要注意的是,如果你愿意,可以用类中定义的其他方法来替代 __call__ 的名字:

>>> class Foo(object):
...     def tick(self):
...         print ("something")
...     __call__ = tick
... 
>>> a = Foo()
>>> a()
something
>>> a.tick()
something
31

tick(self) 改成 __call__(self) 是正确的做法。

这和内存分配没有关系。__call__ 的意思是,当你用一个对象 foo 这样调用它:foo() 时,Python 会调用的那个函数。

关于你后面的问题:要检查某个东西是不是对象,可以用 isinstance 或者 issubclass(可能还要用到 object 类)。在我看来,这个回答 比你可能见过的关于“我怎么判断某个东西是不是函数?”的问题的接受回答要好。

撰写回答