面向对象编程之类的简单双下划线方法
面向对象编程之类的简单双下划线方法
八股文如下:
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__()方法, 直接 对象()
可以看到,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
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__
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 函数和父类没有实质性的关联.
正式讲解如下:
'''
1、len方法:
判断长度。
2、hash方法:
python中hash是一个算法函数,又称哈希算法;
主要指把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值,能够应用于“密码”、“文件完整性校验”、“数字签名”等方向。
3、eq方法:
判断两个等号就会触发 ==
4、item方法:
把对象变成一个字典dict,可以像字典一样进行增删改查
'''
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def __len__(self):
print('字符串长度!')
return 2
def __hash__(self):
print('hash值!!')
return 222
def __eq__(self, other):
print(self.name,other.name)
def __getitem__(self, item):
print('get item:',self.__dict__[item])
#增加和修改都可以用setitem方法!!
def __setitem__(self, key, value):
print('set_item!!')
self.__dict__[key]=value
def __delitem__(self, key):
print('删除属性!')
del self.__dict__[key]
def __delattr__(self, item):
print('删除属性方法!!')
self.__dict__.pop(item)
p1=Person('小明','11')
p2=Person('小李','11')
len(p1)
print(hash(p1))
print(p1==p2)
p1['name']
p1['sex']='man'
p1['student']='小李'
print(p1.sex)
del p1['sex']
#print(p1.sex)
del p1.student
print(p1.student)
1、len方法:
判断长度。
2、hash方法:
python中hash是一个算法函数,又称哈希算法;
主要指把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值,能够应用于“密码”、“文件完整性校验”、“数字签名”等方向。
3、eq方法:
判断两个等号就会触发 ==
4、item方法:
把对象变成一个字典dict,可以像字典一样进行增删改查