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) 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): self.x = 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 函数和父类没有实质性的关联.