Python __call__()

发布于 2020-06-03  687 次阅读


python中一切皆对象,函数也是对象,同时也是可调用对象(callable),关于可调用对象自定义函数、内置函数、类都是可调用对象,但凡是可以把一对小括号应用到某个对象上面"对象名()"都可称之可调用对象,判断一个对象是否可调用对象可用callable函数来判断

>>> class A: 
...     pass
...
>>> callable(A) # 类是可调用对象
True
>>> callable(int) # 内置函数是可调用对象
True
>>> def test(): 
...     pass
...
>>> callable(test) # 自定义函数是可对用对象
True
>>> a=A()
>>> callable(a) # 类实例对象不是可调用对象
False

__callable__()作用

一个类实例对象想变成可调用对象,需要加一个__callable__()方法

为了将类的实例对象变成可调用对象(相当于重载()运算符),使得类实例对象可以像调用普通函数那样,以"对象名()"的方式调用。

class A:
    def __call__(self, *args, **kwargs):
        print('__call__在执行--------', args)
a=A()
a('chenshiyang','www.chenshiyang.com')

####################################################
__call__在执行-------- ('chenshiyang', 'www.chenshiyang.com')

可以看到,通过在 A类中实现 __call__() 方法,使的 a实例对象变为了可调用对象。

Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象。可调用对象包括自定义的函数、Python 内置函数以及本节所讲的类实例对象。

对于可调用对象,实际上“名称()”可以理解为是“名称.__call__()”的简写。仍以上面程序中定义的 clangs 实例对象为例,其最后一行代码还可以改写为如下形式:

a.__call__("chenshiyang","www.chenshiyang.com")

运行程序会发现,其运行结果和之前完全相同。

这里再举一个自定义函数的例子,例如:

def say():
    print('chenshiyang你是个帅哥!')

say()
say.__call__()
###########################################
chenshiyang你是个帅哥!
chenshiyang你是个帅哥!

类中方法调用

class B:
    def say(self):
         print('chenshiyang你是个帅哥!')

b=B()
b.say()
b.say.__call__()
####################################
chenshiyang你是个帅哥!
chenshiyang你是个帅哥!

__call__()其它作用

实例对象也可以像函数一样作为可调用对象来用,那么,这个特点在什么场景用得上呢?这个要结合类的特性来说,类可以记录数据(属性),而函数不行(闭包某种意义上也可行),利用这种特性可以实现基于类的装饰器,在类里面记录状态,比如,下面这个例子用于记录函数被调用的次数:

class Counter:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        return self.func(*args, **kwargs)

# @Counter
def foo():
    pass

foo = Counter(foo) # 这时foo已经是Counter实例而不是foo函数

for i in range(10):
    foo() # 执行Counter __call__方法

print(foo.count)  # 10

用 __call__() 弥补 hasattr() 函数的短板, 之前介绍了 hasattr() 函数的用法,该函数的功能是查找类的实例对象中是否包含指定名称的属性或者方法,但该函数有一个缺陷,即它无法判断该指定的名称,到底是类属性还是类方法

要解决这个问题,我们可以借助可调用对象的概念。要知道,类实例对象包含的方法,其实也属于可调用对象,但类属性却不是。举个例子:

class P:
    def __init__ (self):
        self.name = "chenshiyang"
        self.add = "www.chenshiyang.com"
        
    def say(self):
        print("我正在学Python")

p = P()
if hasattr(p, "name"):
    print(hasattr(p.name, "__call__"))

print("**********")

if hasattr(p,"say"):
    print(hasattr(p.say,"__call__"))


############################################
False
**********
True

参考链接:


一名测试工作者,专注接口测试、自动化测试、性能测试、Python技术。