如何判断一个类是否有一个方法`__call__`?

时间:2017-09-27 20:20:43

标签: python python-3.x

一个非常简单的类不是可调用的类型:

>>> class myc:
...     pass
... 
>>> c=myc()
>>> callable(c)
False

如何判断某个类是否有方法__call__?为什么以下两种方式会产生相反的结果呢?

>>> myc.__call__
<method-wrapper '__call__' of type object at 0x1104b18>     

>>> __call__ in myc.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '__call__' is not defined

感谢。

2 个答案:

答案 0 :(得分:5)

myc.__call__为您提供__call__方法,用于调用myc本身,而不是myc的实例。这是你执行

时调用的方法
new_myc_instance = myc()

不是你做的时候

new_myc_instance()

__call__ in myc.__dict__给你一个NameError,因为你忘了在__call__周围加上引号,如果你记得要加上引号,那么它会给你假,因为myc.__call__不是通过myc.__dict__找到了。它是通过type(myc).__dict__(a.k.a。type.__dict__找到的,因为type(myc) is type)。

要检查myc是否有针对其实例的__call__实施,您可以执行手动MRO搜索,但collections.abc.Callable已经为您实现了这一点:

issubclass(myc, collections.abc.Callable)

答案 1 :(得分:1)

myc.__call__给你回复 1 而不是提出AttributeError的原因是因为myctype)的元类有一个__call__方法。如果在实例上找不到某个属性(在本例中为您的类),则会查找该类(在本例中为元类)。

例如,如果您有自定义元类,__call__查找将返回不同的内容:

class mym(type):
    def __call__(self, *args, **kwargs):
        return super().__call__(*args, **kwargs)

class myc(metaclass=mym):
    pass

>>> myc.__call__
<bound method mym.__call__ of <class '__main__.myc'>>

关于已经在评论中回答的__call__ in myc.__dict__和已经回答的其他答案:您刚忘记引文:

>>> '__call__' in myc.__dict__
False

然而,myc也可以是一个可调用类的子类,在这种情况下它也会给False

class mysc:
    def __call__(self):
        return 10

class myc(mysc):
    pass

>>> '__call__' in myc.__dict__
False

所以你需要更强大的东西。就像搜索所有超类一样:

>>> any('__call__' in cls.__dict__ for cls in myc.__mro__)
True

或者,正如user2357112所指出的,collections.Callable使用__call__,以便找到>>> from collections.abc import Callable >>> issubclass(myc, Callable) True

hasattr(myc, '__call__')

1 这也是您不能仅使用__call__查明其本身是否有var date = moment('2017-09-27'); 方法的原因。