python的魔术方法
一、魔术方法特殊属性
- __name__ : 类、函数、方法等的名字
- __module__: 定义所在的模块名
- __class__ : 对象或类所属的类
- __bases__ : 类的基类的元组,顺序为它们在基类列表中出现的顺序
- __doc__ : 类、函数的文档字符串,如果没有定义则为None
- __mro__ :类的mro,class.mro()返回结果的保存在__mro__中
- __dict__ : 类或实例的属性,可写的字典
二、查看属性
1、__dir__方法:
- 返回类或者对象的所有成员名称列表,dir()函数就是调用__dir__方法
- 如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中收集信息
2、dir()对应不同类型的对象具有不同的行为:
- 如果对象是模块对象,列表包含模块的属性名
- 如果对象是类型或类对象,列表包含类的属性名,机它的基类的属性名
- 否则,列表包含对象的属性名,它的类的属性名和类的基类的属性名
三、魔术方法的分类
- 创建与销毁:__init__与__del__
- hash:__hash__方法:内建函数hash()调用的返回值,返回一个整数,如果定义这个方法该类的实例就可hash
举例: class A: def __init__(self): self.a = 'a' self.b = 'b' def __hash__(self): return 1 def __eq__(self,other): return self.a = other.a print(hash(A())) print((A(),A())) print({A(),A()}) s = {A(),A()} # set集合 print(s) 执行输出: 1 (<__main__.A object at 0x0000000004E4C780>, <__main__.A object at 0x0000000004E4C860>) {<__main__.A object at 0x0000000004E4C6D8>, <__main__.A object at 0x0000000004E4C860>} {<__main__.A object at 0x0000000004E4C780>, <__main__.A object at 0x0000000004E4C6D8>} 上例set为什么不能剔除相同的key
1、__eq__方法:对应==操作符,判断2个对象是否相等,返回值bool
class A: def __init__(self): self.a = 'a' self.b = 'b' def __hash__(self): return 1 def __eq__(self,other): return self.a == other.a print(hash(A())) print((A(),A())) print({A(),A()}) s = {A(),A()} # set集合 print(s) 执行输出: 1 (<__main__.A object at 0x0000000004E4C9B0>, <__main__.A object at 0x0000000004E4CB00>) {<__main__.A object at 0x0000000004E4CB38>} {<__main__.A object at 0x0000000004E4C9B0>} __hash__方法只是返回一个hash值作为set的key,但是去重还需要__eq__来判断2个对象是否相等 hash值相等,只是hash冲突,不能说明两个对象是相等的,因此,一般来说提供__hash__方法是为了作为set或者dict的key的,所以去重要同时提供__eq__方法 可hash对象必须提供__hash__方法,没有提供的话,isinstance(p1,collections.Hashable)一定为False
2、list类为什么不可hash?
- 源码中有一句__hash__ = None,也就是说如果调用__hash__()相当于None(),一定报错
- 所有类都继承object,而这个类是具有__hash__()方法的,如果一个类不能被hash,就是把__hash__设置为None了
from collections import Hashable class Point: def __init__(self,x,y): self.x = x self.y = y def __hash__(self): return hash((self.x,self.y)) def __eq__(self,other): return self.x == other.x and self.y == other.y p1 = Point(4,5) p2 = Point(4,5) print(hash(p1)) print(hash(p2)) print(p1 is p2) print(p1 == p2) print(set((p1,p2))) 执行输出: 3713084879518070856 3713084879518070856 False True {<__main__.Point object at 0x0000000004E5C2E8>}
3、bool方法
- __bool__方法: 内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值
- 没有定义__bool__(),就找__len__()返回长度,非0为真,如果__len__()也没有定义,那么所有实例都返回真
举例: class A: pass print(bool(A())) #没有定义 class B: def __bool__(self): # 定义了返回函数返回值 return False print(bool(B)) print(bool(B())) class C: def __len__(self): #定义了len return 0 print(bool(C()))
四、可视化
- __repr__ : 内建函数repr()对一个对象获取字符串表达,调用__repr__方法返回字符串表达;如果__repr__没有定义,就直接返回object的定义就是显示内存地址信息
- __str__ : str()函数,内建函数format,print()函数调用,需要返回对象的字符串表达;如果没有定义,就去调用__repr__方法返回字符串表达,如果__repr__没有定义,就直接返回对象的内存地址信息
- __bytes__ : bytes的时候,返回一个对象的bytes表达,即返回bytes对象
举例: class A: def __init__(self): self.a = 'a' self.b = 'b' def __repr__(self): return 'repr:{},{}'.format(self.a,self.b) def __str__(self): return 'str:{},{}'.format(self.a,self.b) print(A()) # 优先使用__str__函数 print([A()]) # []使用__str__,但其内部使用__repr__ print(([str(A())])) #[]使用__str__,str()函数也是用__str__ print('str:a,b') s = 'b' print(['a'],(s,)) 执行输出: str:a,b [repr:a,b] ['str:a,b'] str:a,b ['a'] ('b',)
五、运算符重载
- operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符类操作
- 比较运算符:__lt__,__le__,__eq__,__gt__,__ge__,__ne__
- 算数运算符、移位:__add__,__sub__,__mul__,
举例: class A: def __init__(self,x): self.x = x def __sub__(self,other): return self.x - other.x def __isub__(self,other): tmp = self.x - other.x return A(tmp) def __str__(self): return str(self.x) x = A(5) y = A(4) print(x,y) print(x-y,x.__sub__(y)) x -= y print(x) 完成Point类设计,实现判断点相等的方法,并完成向量的加法 class Point: def __init__(self,x,y): self.x = x self.y = y def add(self,other): return Point(other.x + self.x, other.y + self.y) def __add__(self,other): return (self.x + other.x, self.y + other.y) def __eq__(self,other): return self.x == other.x and self.y == other.y def __str__(self): return "Point:{},{}".format(self.x,self.y) p1 = Point(1,1) p2 = Point(2,2) points = (p1,p2) print(points[0].add(points[1])) print('=========运算符重载============') print(points[0] + points[1]) print(Point(*(points[0] + points[1]))) print(p1 == p2) 运算符重载应用场景 往往是用面向对象实现的类,需要做大量的运算符是这样运算在数学上最常见的表达方式 提供运算符重载,比直接提供加法方法要更加合适该领域内使用者的习惯