对__call__方法的一些思考和验证

本文深入探讨Python中__call__方法的作用,解释如何使类实例变得可调用,以及在元类和类中调用__call__的机制。通过实例演示,展示了__call__在自定义类和元类中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实际上,一个函数(甚至对象)之所以能执行,关键就在于 call() 方法。实际上xx(arg1, arg2,…) 只是xx.call(arg1, arg2, …) 的快捷写法,因此我们甚至可以为自定义类添加 call 方法,从而使得该类的实例也变成可调用的。

定义了__call__方法才能以使用函数的方式执行,用实例()方式调用

例如:
class A(object):
    def __call__(self):
        print ("__call__ is running")

a = A()
a()
A()()


#输出
#__call__ is running
#__call__ is running

但是在创建对象的时候会自动调用元类中的call,思考一下???,如下demo

class Singleton(type):
    ...:     def __call__(cls, *args, **kwargs):
    ...:         print("call is running")
    ...:         if not hasattr(cls, '_instance'):
    ...:             cls._instance = super(Singleton, cls).__call__(*args, **kwar
    ...: gs)
    ...:         return cls._instance

    ...:     
In [69]: class P(metaclass=Singleton):
    ...:     ...:     pass
...: 

创建p1、p2实例对象,会自动调用call方法
I

n [70]: p1 =P()
call is running

In [71]: p2 = P()
call is running

原因:
class XXX():pass #这是创建对象,创建类对象
x = XXX() #这里有两步,一步是调用创建类对象XXX的类(元类)中的call方法,第二步是call再去调XXX类中的new,init方法进行初始化得到x实例对象
x() #这里是去调用创建x实例对象的类(XXX类)中的call方法
当一个对象后面加上()调用的时候,注意是调用不是定义(即创建)的时候,实际上是去调用创建该对象的类的__call__()方法,这里还分两种情况:
1、如果是实例对象则在创建它的本类中找call方法,找到则调用,本类中没有则继续在它的父类中找,如果父类中没有则调用失败,无法用xxobj() (#xxobj是个实例对象)的方式使用。
2、如果是类对象则在创建它的元类中找call方法,找到则调用,元类中没有则调用失败,无法用XXobj()的方式使用,比如A() ( #A是一个类对象)。因为元类已经是顶级的类了,没有继承。
总而言之,一个元类(元类中只能自己定义)或类(不管是继承过来的还是本类中自己定义的)中有什么,那么创建出来的对象就有什么就能用什么,只不过实例属性和方法是每个实例独有的,类的属性和方法是所有实例共享的。
①验证元类中的类属性对多个类对象是否共享?
是的,但是类对象再创建出来的对象不能访问元类中的属性,只能通过类对象来访问
看以下demo:

#!/usr/bin/python3

"""①验证元类中的类属性对多个类对象是否共享?"""


class mym(type):

    x = 20

def smm(self):
    print("i am smm")

@classmethod
def cmm(cls):
    print("i am cmm")

@staticmethod
def stmm():
    print("i am stmm")

def __new__(cls,*args,**kwargs):
    print("in new method")
    return super().__new__(cls,*args,**kwargs)

def __init__(self,*args,**kwargs):
    print("in init method")
    #super().__init__(self,*args,**kwargs)


class A(metaclass=mym):
    y = 10
    
def aa(self):
    print("in aa")
    print("A.x=",A.x)
    A.smm()
    A.cmm()
    A.stmm()

print("A.x=",A.x)
A.smm()
A.cmm()
A.stmm()
print("A.y:",A.y)
a = A()
a.aa()
print("mym.x=",mym.x)
mym.x=522
A.x=999
print("A.x=",A.x)
print("---mym.x=",mym.x)
#a.cmm()  # 类对象A创建出来的实例对象a不能访问元类中的方法和属性,打开会报错
print("a.x:",a.x)
print("--"*20)


class B(metaclass=mym):
    pass

print("B.x=",B.x)
B.x=2000
print("----B.x=",B.x)
del A.x
print("----A.x=",A.x)
b = B()
print("b.x=",b.x)

执行结果:

in new method
in init method
A.x= 20
i am smm
i am cmm
i am stmm
A.y: 10
in aa
A.x= 20
i am smm
i am cmm
i am stmm
mym.x= 20
A.x= 999
---mym.x= 522
a.x: 999
----------------------------------------
in new method
in init method
B.x= 522
----B.x= 2000
----A.x= 522
b.x= 2000

②验证类名()、类名(args) 实际上就是调用元类中的call方法?然后调用new方法并放回对象,再调用其init方法?
是的,看以下demo

#!/usr/bin/python3
  2 
  3 
  4 # ②验证类名()、类名(args) 实际上就是调用元类中的call方法?
  5 #然后调用new方法并放回对象,再调用其init方法?
  6 
  7 
  8 class mym(type):
  9 
 10     x=1
 11     def __call__(self,*args,**kwargs):
 12         print("in  mym call method")
 13         return object.__new__(self,*args,**kwargs)
 14     
 15 
 16 class A(metaclass=mym):
 17     y = 2
 18     def test(self):
 19         print("in A test method")
 20     
 21 
 22 class B(metaclass=mym):
 23     z = 3
 24     def __init__(self,a):
 25         self.a = a
 26         print("in B init method")
 27     
 28 
 29 a = A()  # 类对象名()调用创建该对象的类中的call方法
 30 print(type(a))
 31 a.test()
 32 print("0"*32)
 33 print("a.y=",a.y)
 34 b = B(10000)  # 类对象名()调用创建该对象的类中的call方法
 35 print(type(b))
 36 print("b.z=",b.z)

输出

in  mym call method
<class '__main__.A'>
in A test method
00000000000000000000000000000000
a.y= 2
in  mym call method
<class '__main__.B'>
b.z= 3

e.p

class O():
     ...:     x = 10
     ...:     def __call__(cls):
     ...:         print("call is running")
     ...:         
class P(O):
     ...:     y =20
     ...:     def bb(self):
     ...:         print("bb is running")

执行输出:

 p = P()
p()
call is running

e.p
定义一个元类

class A(type):
     ...:     x = 10
     ...:     def call(self):
     ...:         print("c is running")
     ...:     def __call__(self):
     ...:         print("__call__ is running")
     ...:         print(self)
     ...:     def __new__(cls,*args,**kwargs):
     ...:         print("new is running")
     ...:         print(cls)
     ...:         return type.__new__(cls,*args,**kwargs)

通过元类A创建一个类对象B:

class B(metaclass=A):
     ...:     pass 

此时输出:

new is running
<class '__main__.A'>

In [113]: type(B)
Out[113]: __main__.A

In [114]: type(A)
Out[114]: type

创建B的实例对象:

b = B()  #注意这里用的是 对象.()

此时输出:

__call__ is running  # 元类A中的call方法被调用了
<class '__main__.B'>

再创建一个对象时仍会调用call方法:

In [117]: c = B()
__call__ is running
<class '__main__.B'>

参考博客:https://www.cnblogs.com/ellisonzhang/p/10513238.html
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
https://www.jianshu.com/p/2e2ee316cfd0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值