Python()-类的专有⽅法之双下划线⽅法
1. __call__() ⽅法
对象+() 可以直接调⽤__call__()⽅法,类似普通函数的调⽤
class CallTest(object):
def__init__(self):
print('I am __init__')
def__call__(self):
print('I am __call__')
return True
def run(self):
print('I am run')
return True
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__() ⽅法
----调⽤(获取)对象属性
class Foo:
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__() ⽅法
----删除对象属性
class Foo:
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__() ⽅法
----设置属性: 增加对象属性, 修改对象属性
class Foo:
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__⽅法
class Foo:
a = 1
def__init__(self, b):
self.b = b  # 赋值属性操作
def__setattr__(self, key, value):
print("run __setattr__")
f = Foo(100)  # 如果实例化的时候传⼊参数进⾏赋值属性操作,  触发__setattr__()⽅法
# >> 输出: run __setattr__
设置属性时, ⽅法内部必须操作类的属性字典
class Foo:
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__()⽅法后,⽅法内部如果不进⾏属性字典的操作,那么除⾮直接操作属性字典,否则永远⽆法完成赋值class Foo:
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__()⽅法实现我们⾃定义的功能
class Foo:
a = 1
dic = {}  # ⾃定义⼀个空字典
def__setattr__(self, key, value):
self.dic[key] = value
print("run __setattr__")
f = Foo()
f.y = 3
# >> 输出: run __setattr__
print(f.dic)  # 给类变量dic添加键值对
# >> 输出: {'y': 3}
print(f.__dict__)  # 类属性不发⽣变化
# >> 输出: {}
⼀个⼩⽰例:
class Foo:
def__init__(self, dic):
self._dic = dic
def__getattr__(self, item):
val = self._dic[item]
if isinstance(val, dict):
a = Foo(val)
getattribute方法返回类型
return a  # 重点: ⼜返回⼀个对象
else:
return val
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'}),这个对象可以继续通过点(.)调⽤对象的属性,如果有多层嵌套,⼀直循环下去
接着上⾯的例⼦继续:
def v2(arg):
return arg
dic = {'k1': {'k2': v2}}
dic = Foo(dic)
ret = dic.k1.k2(100)
print(ret)
# >> 输出: 100
6. __getattribute__() ⽅法
长得和__getattr__那么像,那么__getattribute__与之有什么关系呢?
class Foo:
a = 1
def__init__(self, x):
def__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
class Foo:
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 的⼯作原理如下:
def super(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 函数和⽗类没有实质性的关联.