实际上,一个函数(甚至对象)之所以能执行,关键就在于 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