1. __call__() 方法
对象+() 可以直接调用__call__()方法 , 类似普通函数的调用
classCallTest(object):def __init__(self):print('I am __init__')def __call__(self):print('I am __call__')returnTruedefrun(self):print('I am run')returnTrue
obj=CallTest()
obj.run()#调用普通方法 对象.func_name()
obj() #调用__call__()方法, 直接 对象()
打印结果:
I am __init__
I am run
I am __call__
可以看到,obj这个对象被实例化出来,如果要调用__call__方法的话,直接obj(),即可调用并返回结果。obj就类似一个函数地址,obj()即执行这个函数。
2. __init__() 方法
构造函数,在生成对象时调用
===========================
__getattr__, __setattr__, __delattr__
1. 调用对象的一个不存在的属性时会触发__getattr__方法
2. 删除对象的一个属性的时候会触发__delattr__方法
3. 设置对象(增加/修改)属性会触发__setattr__方法
设置对象属性和删除对象属性会触发__setattr__ 和 __delattr__ 方法,但要注意的是,在调用这两个方法时,方法内部必须操作类的属性字典,否则会造成无限递归
3. __getattr__() 方法
----调用(获取)对象属性
classFoo:
a= 1
def __getattr__(self, item):print('run __getattr__')
f=Foo()print(f.a) #属性存在,就不会触发__getattr__()方法#>> 输出: 1
print(f.b) #只有在使用点调用属性且属性不存在的时候才会触发,并且返回None#>> 输出: run__getattr__#>> 输出: None
4. __delattr__() 方法
----删除对象属性
classFoo:def __delattr__(self, item):print('run __delattr__')#del self.item # 这样会造成无限递归
self.__dict__.pop(item)
f=Foo()
f.a= 3
print(f.__dict__) #>> 输出: {'a': 3}
print(f.a) #>> 输出: 3
del f.a #>> 输出: run __delattr__
print(f.a) #>> 报错: AttributeError: 'Foo' object has no attribute 'a'
5. __setattr__() 方法
----设置属性: 增加对象属性, 修改对象属性
classFoo:
a= 1
def __setattr__(self, key, value):print("run __setattr__")
f=Foo()#没有赋值,什么都不会发生
f.c= 200 #如果增加类属性, 触发触发__setattr__()方法#>> 输出: run__setattr__
f.a= 2 #如果修改类属性, 触发触发__setattr__()方法#>> 输出: run __setattr__
实例化对象传参,会触发__getattr__方法
classFoo:
a= 1
def __init__(self, b):
self.b= b #赋值属性操作
def __setattr__(self, key, value):print("run __setattr__")
f= Foo(100) #如果实例化的时候传入参数进行赋值属性操作, 触发__setattr__()方法#>> 输出: run __setattr__
设置属性时, 方法内部必须操作类的属性字典
classFoo:
a= 1
def __setattr__(self, key, value):#self.key = value # 增加/修改类属性,会触发__setattr__()方法,如果这个操作在setattr方法内部,会造成无限递归
self.__dict__[key] = value #使用这种方法会完成增加/修改类属性的操作
print("run __setattr__")
f=Foo()
f.y= 3 #增加/修改类属性,调用__setattr__()方法#>> 输出: run __setattr__
print(f.__dict__)#>> 输出: {'y': 3}
当我们重写__setattr__()方法后,方法内部如果不进行属性字典的操作,那么除非直接操作属性字典,否则永远无法完成赋值
classFoo:def __setattr__(self, key, value):print("run __setattr__")
f=Foo()
f.y= 3 #设置对象属性,调用__setattr__()方法,而__setattr__()方法什么都没干,所以完成不了对象的设置属性操作#>> 输出: run __setattr__
print(f.__dict__)#>> 输出: {}
print(f.y) #完成不了赋值#>> 报错: AttributeError: 'Foo' object has no attribute 'y'
理解了__setattr__()方法的原理,我们就可以利用 __setattr__()方法 实现我们自定义的功能
classFoo:
a= 1dic= {} #自定义一个空字典
def __setattr__(self, key, value):
self.dic[key]=valueprint("run __setattr__")
f=Foo()
f.y= 3
#>> 输出: run __setattr__
print(f.dic) #给类变量dic添加键值对#>> 输出: {'y': 3}
print(f.__dict__) #类属性不发生变化#>> 输出: {}
一个小示例:
classFoo:def __init__(self, dic):
self._dic=dicdef __getattr__(self, item):
val=self._dic[item]ifisinstance(val, dict):
a=Foo(val)return a #重点: 又返回一个对象
else:returnval
dic= {'k1': 'v1', 'k2': 'v2'}
dic=Foo(dic)print(dic.k1)#>>输出: v1
print(dic.k2)#>>输出: v2
dic= {'k1': {'k2': 'v2'}}
dic=Foo(dic)print(dic.k1)#>>输出: 一个对象 <__main__.Foo object at 0x00000000024D7F98>
print(dic.k1.k2) #对象可以继续点(.)取属性操作#>>输出: v2
原理:
Foo(dic)实例化一个对象, dic.k1触发__getattr__()方法, val={'k2': 'v2'},当val值为一个字典对象时,if条件成立, 返回一个以val字典为参数的对象,就是说: dic.k1 == Foo({'k2': 'v2'}),这个对象可以继续通过点(.)调用对象的属性,如果有多层嵌套,一直循环下去
接着上面的例子继续:
defv2(arg):returnarg
dic= {'k1': {'k2': v2}}
dic=Foo(dic)
ret= dic.k1.k2(100)print(ret)#>> 输出: 100
6. __getattribute__() 方法
长得和__getattr__那么像,那么__getattribute__与之有什么关系呢?
classFoo:
a= 1
def __init__(self, x):
self.x=xdef __getattribute__(self, item):print('不管属性[%s]是否存在,我都会执行' %item)
f= Foo(100)print(f.a)#>>输出: 不管属性[a]是否存在,我都会执行#>>输出: None
print(f.b)#>>输出: 不管属性[b]是否存在,我都会执行#>>输出: None
print(f.x)#>>输出: 不管属性[x]是否存在,我都会执行#>>输出: None
当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError
classFoo:def __getattr__(self, item):print('run __getattr__')def __getattribute__(self, item):print('不管属性[%s]是否存在,我都会执行' %item)#raise AttributeError('啦啦啦啦')
f=Foo()#print(f.a)#>>输出: 不管属性[a]是否存在,我都会执行#>>输出: None
print(f.a) #打开注释,手动抛错: raise AttributeError('q')#>>输出: 不管属性[a]是否存在,我都会执行#>>输出: run __getattr__#>>输出: None
7. super()
super 的工作原理如下:
defsuper(cls, inst):
mro= inst.__class__.mro()return mro[mro.index(cls) + 1]
其中 cls 代表类, inst 代表实例, super 函数做了两件事:
1. 获取实例对象 inst 的类的 MRO 列表
2. 查找 cls 在当前 MRO 列表中的 index ,并返回它的下一个类,即 mro[index + 1]
当使用 super(cls, inst) 时, Python 会在 inst 的 MRO 列表上搜索 cls 的下一个类. 可以看出, 事实上 super 函数和父类没有实质性的关联.