欢迎访问的个人博客,博客地址为 https://haibei.online/
〇、魔术方法定义在python语法中,往往你会看到有的名称前面和后面都加上了双下划线,由这些名字组成的集合所包含的方法就叫做魔法方法 ,也叫做特殊方法。 python的魔术方法非常强大,然而随之而来的则是责任。了解正确的方法去使用非常重要! 一、总述 1、class类的老大哥 __new__
之所以称之为老大哥,是因为 __new__
在类执行时第一个被调用,而不是 __init__
如果 __new__
返回值为None,那么 __init__
根本不会被调用。 举个例子 1 2 3 4 5 6 7 8 class A (object) : def __new__ (cls) : print("__new__方法被执行" ) return super().__new__(cls) def __init__ (self) : print("__init__方法被执行" ) a = A()
结果如下:
1 2 __new__方法被执行 __init__方法被执行
2、实例销毁: __del__
__del__(self)
:当一个实例被销毁的时候调用的方法
3、类语法糖: __call__
__call__(self[, args...])
允许一个类的实例像函数一样被调用: x(a, b)
调用 x.__call__(a, b)
3.1、演示代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class A (object) : def __init__ (self, name) : self.name = name print(f"{name} 被创造出来了了" ) def __call__ (self, *args, **kwargs) : """允许一个类的实例像函数一样被调用""" print(f"{self.name} 打了个响指,一共{len(args)} 名超级英雄消失,他们分别是{'、' .join(args)} 等。" ) def __del__ (self) : """程序结束(销毁)后,会自动执行""" print(f"{self.name} 被销毁了" ) if __name__ == '__main__' : a = A("灭霸" ) a("蜘蛛侠" , "幻世" , "黑豹" , "奇异博士" )
结果如下:
1 2 3 灭霸被创造出来了了 灭霸打了个响指,一共4名超级英雄消失,他们分别是蜘蛛侠、幻世、黑豹、奇异博士等。 灭霸被销毁了
二、调用系统方法语法 说明 符号 __len__(self)
定义当被 len
调用时的行为 len() __repr__(self)
定义当被 repr
调用时的行为 repr() __str__(self)
定义当被 str
调用时的行为 str() __bytes__(self)
定义当被 bytes
调用时的行为 bytes() __hash__(self)
定义当被 hash
调用时的行为 hash() __bool__(self)
定义当被 bool
调用时的行为 bool() __format__(self, format_spec)
定义当被 format
调用时的行为 format()
1、 __repr__
和 __str__
的区别当一个类中没有使用 __repr__
和 __str__
这两个魔法函数, print()
直接打印实例会返回类名和内存地址
__repr__
和 __str__
这两个方法都是用于显示的, __str__
是面向用户的,而 __repr__
面向程序员。
打印操作会首先尝试 __str__
,它通常应该返回一个友好的显示。
在交互环境下, __repr__
会生效,而 __str__
不会起作用。直接执行实例变量名即可打印,无需 print()
。
2、参考代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class A (object) : """对照组:没有使用__str__,打印结果为类型名及其内存地址""" def __init__ (self, info) : self.info = info class B (object) : """标准组:使用__str__了,打印结果为__str__魔法函数的返回值""" def __init__ (self, info) : self.info = info def __str__ (self) : return self.info + f"我是{__class__.__name__} 类,我用了__str__的魔法函数,真香!" if __name__ == '__main__' : demo_message = "真氪金定律:程序员的能力与发量成反比!快拔头发吧!" a = A(demo_message) b = B(demo_message) print(a) print(b)
显示结果如下:
1 2 <__main__.A object at 0x1097bf438> 真氪金定律:程序员的能力与发量成反比!快拔头发吧!我是B类,我用了__str__的魔法函数,真香!
2.1、什么是交互环境交互环境的典型特点是写完一行,执行一行。非交互环境要一次执行所有程序
直接在Terminal中启动python,就是交互环境 Jupyter Notebook和Lab都是交互环境 Pycharm中的python Console是交互环境 Anaconda Spyder中提供的Console也是交互环境 2.2、在交互环境中 __repr__
和 __str__
代码如下:
可以看出,5行中直接执行 a
因为定义了 __repr__
,在交互环境下是可以正确显示的; 而在7行中,直接执行 b
是无法正确显示的。 三、有关属性语法 说明 __getattr__(self, name)
定义当用户试图获取一个不存在的属性时的行为 __getattribute__(self, name)
定义当该类的属性被访问时的行为 __setattr__(self, name, value)
定义当一个属性被设置时的行为 __delattr__(self, name)
定义当一个属性被删除时的行为 __dir__(self)
定义当 dir() 被调用时的行为 __get__(self, instance, owner)
定义当描述符的值被取得时的行为 __set__(self, instance, value)
定义当描述符的值被改变时的行为 __delete__(self, instance)
定义当描述符的值被删除时的行为
1、 __getattr__
和 __setattr__
存在的问题__getattr__
和 __setattr__
这两个方法 __init__
中变量赋值事,就会默认被创建。举个例子:传统赋值方式是 self.a="a"
, __getattr__
和 __setattr__
就会默认创建出来。 所以当在 __getattr__
中使用 getattr()
或者 self.xxx
, 程序会陷入无限递归。 防止递归解决方式有两种,但原理相同。使用超类赋值或者通过超类进行访问超类赋值(新式类):super().setattr (“xxx”, xxx) 超类访问(新式类):super().getattr (“xxx”) 2、演示代码代码功能:伪造一个类,使其具有与 dict
类型变量相同的功能; 同时,该类还具备通过’点’来访问dict的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class A (object) : """伪造字典""" def __init__ (self, **kwargs) : super().__setattr__("dict_like" , kwargs) def __getattr__ (self, name) : """可以调用dict类的系统方法""" print("__getattr__被执行:" , end="" ) if name in self.dict_like: print(f"\t访问{name} 变量" , end="" ) return self.dict_like[name] else : print(f"\t访问{name} 方法" , end="" ) return getattr(self.dict_like, name) def __setattr__ (self, key, value) : """可以通过'点'的方式,设置字典内容""" print("__setattr__被执行:" , end="" ) self.dict_like[key] = value def __delattr__ (self, item) : print("__delattr__被执行:" , end="" ) del self.dict_like[item] def __len__ (self) : print("__len__被执行:" , end="" ) return len(self.dict_like) def __str__ (self) : print("__str__被执行:" , end="" ) return str(self.dict_like) if __name__ == '__main__' : a = A(a=1 , b=2 , c=3 , d=4 , e=5 , f=6 , g=7 ) print(a) print(len(a)) a.h = 8 print(a) print("\t|\t访问b元素:" , a.b) print(list(a.items())) del a.f print(a)
结果如下:
1 2 3 4 5 6 __str__被执行:{'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4, 'e' : 5, 'f' : 6, 'g' : 7} __len__被执行:7 __setattr__被执行:__str__被执行:{'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4, 'e' : 5, 'f' : 6, 'g' : 7, 'h' : 8} __getattr__被执行:访问b变量 | 访问b元素: 2 __getattr__被执行:访问items方法[('a' , 1), ('b' , 2), ('c' , 3), ('d' , 4), ('e' , 5), ('f' , 6), ('g' , 7), ('h' , 8)] __delattr__被执行:__str__被执行:{'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4, 'e' : 5, 'g' : 7, 'h' : 8}
四、比较操作符语法 说明 符号 __lt__(self, other)
定义小于号的行为 x < y __le__(self, other)
定义小于等于号的行为 x <= y __eq__(self, other)
定义等于号的行为 x == y __ne__(self, other)
定义不等号的行为 x != y __gt__(self, other)
定义大于号的行为 x > y __ge__(self, other)
定义大于等于号的行为 x >= y
五、算数运算符可以让类使用算数预算符进行操作
1、标准运算符说明 代码 符号 反运算 增强运算 加法 __add__(self, other)
+ √ += 减法 __sub__(self, other)
- √ -= 乘法 __mul__(self, other)
* √ *= 除法 __truediv__(self, other)
/ √ /= 整除 __floordiv__(self, other)
// √ //= 模运算 __mod__(self, other)
% √ %= divmod操作 __divmod__(self, other)
divmod() √ 无 次方 __pow__(self, other[, modulo])
power()或 ** √ **= 左位移 __lshift__(self, other)
<< √ <<= 右位移 __rshift__(self, other)
>> √ >>= 按位与 __and__(self, other)
& √ &= 按位异或 __xor__(self, other)
^ √ ^= 按位或 __or__(self, other)
|
√ |=
2、反运算在标准运算符的前面加上“r”就是反运算 假设 a
和 b
是两个实例,就a而言, a+b
为标准预算,因为 a
在前边; b+a
就是反运算,因为 a
在后面。 如果 a
想使用反运算,就需要定义 __radd__
, 3、增量赋值运算 4、代码实例 4.1、3种运算符的区别1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class A (object) : def __add__ (self, other) : print("A:加法的标准运算" ) def __radd__ (self, other) : print("A:加法的反运算" ) def __iadd__ (self, other) : print("A:加法的增强赋值运算" ) class B : pass a=A() b=B() a+b b+a a+=b
输出结果:
1 2 3 A:加法的标准运算 A:加法的反运算 A:加法的增强赋值运算
4.2、伪造list类,使其具备运算符功能1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class A (object) : """伪造list,通过运算符号,完成增减数据""" def __init__ (self, *args) : self.list_like = list(args) def __add__ (self, other) : print("__add__被执行:" , end="" ) self.list_like.append(other) return self def __radd__ (self, other) : print("__radd__被执行:" , end="" ) self.list_like = [other] + self.list_like return self def __iadd__ (self, other) : print("__iadd__被执行:" , end="" ) self.list_like.append(other) return self def __mul__ (self, other) : print("__mul__被执行:" , end="" ) self.list_like = list(map(lambda x: x * other, self.list_like)) return self def __len__ (self) : return len(self.list_like) def __str__ (self) : return str(self.list_like) if __name__ == '__main__' : a = A(1 , 2 , 3 , 5 , 6 , 7 ) a = a + 10 print(a) a = 0 + a print(a) a += 100 print(a) a = a * 6 print(a)
输出结果:
1 2 3 4 __add__被执行:[1, 2, 3, 5, 6, 7, 10] __radd__被执行:[0, 1, 2, 3, 5, 6, 7, 10] __iadd__被执行:[0, 1, 2, 3, 5, 6, 7, 10, 100] __mul__被执行:[0, 6, 12, 18, 30, 36, 42, 60, 600]
六、一元操作符语法 说明 符号 __neg__(self)
定义正号的行为 -x __pos__(self)
定义负号的行为 +x __abs__(self)
定义绝对值的行为 abs() __invert__(self)
定义按位求反的行为 ~x
1、示例代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class A (object) : """伪造list,具备算法运算符功能""" def __init__ (self, *args) : self.list_like = list(args) def __neg__ (self) : print("__neg__被执行:" , end="" ) self.list_like = list(map(lambda x: -x, self.list_like)) return self def __abs__ (self) : print("__abs__被执行:" , end="" ) self.list_like = list(map(lambda x: abs(x), self.list_like)) return self def __invert__ (self) : print("__invert__被执行:" , end="" ) self.list_like = list(map(lambda x: ~x, self.list_like)) return self def __len__ (self) : return len(self.list_like) def __str__ (self) : return str(self.list_like) if __name__ == '__main__' : a = A(5 , 3 , 16 , 14 , 60 , 3 ) a = -a print(a) a = abs(a) print(a) a = ~a print(a)
输出结果
1 2 3 __neg__被执行:[-5 , -3 , -16 , -14 , -60 , -3 ] __abs__被执行:[5 , 3 , 16 , 14 , 60 , 3 ] __invert__被执行:[-6 , -4 , -17 , -15 , -61 , -4 ]
七、类型转换语法 说明 __complex__(self)
定义当被 complex() 调用时的行为(需要返回恰当的值) __int__(self)
定义当被 int() 调用时的行为(需要返回恰当的值) __float__(self)
定义当被 float() 调用时的行为(需要返回恰当的值) __round__(self[, n])
定义当被 round() 调用时的行为(需要返回恰当的值) __index__(self)
当对象是被应用在切片表达式中时,实现整形强制转换;如果你定义了一个可能在切片时用到的定制的数值型, 你应该定义 index。如果 index 被定义,则 int 也需要被定义,且返回相同的值
八、上下文管理(with 语句)演示代码如下:
1 2 with open("./1.txt" ,"r" ,encoding="utf-8" ) as f: data=f.readlines()
语法 说明 __enter__
1. 定义当使用 with 语句时的初始化行为; 2. __enter__
的返回值被 with 语句的目标或者 as 后的名字绑定 __exit__
1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么 2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
九、容器类型语法 说明 __len__(self)
定义当被 len() 调用时的行为(返回容器中元素的个数) __getitem__(self, key)
定义获取容器中指定元素的行为,相当于 self[key] __setitem__(self, key, value)
定义设置容器中指定元素的行为,相当于 self[key] = value __delitem__(self, key)
定义删除容器中指定元素的行为,相当于 del self[key] __iter__(self)
定义当迭代容器中的元素的行为 __reversed__(self)
定义当被 reversed() 调用时的行为 __contains__(self, item)
定义当使用成员测试运算符( in
或 not in
)时的行为
1、 示例代码1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Avenger (object) : """复仇者联盟""" def __init__ (self, **kwargs) : self.members = kwargs self.names = list(kwargs.keys()) self.index = 0 def __getitem__ (self, item) : fighting_num = self.members[item] print("__getitem__被执行:" , f"正在读取复仇者【{item} 】的信息,其战斗力为{fighting_num} " ) return fighting_num def __setitem__ (self, key, value) : print("__setitem__被执行:" , f"新增复仇者【{key} 】,其战斗力为{value} " ) self.members["key" ] = value def __delitem__ (self, key) : print("__delitem__被执行:" , f"复仇者【{key} 】已阵亡!" ) del self.members["key" ] def __iter__ (self) : print("__iter__被执行" ) return self def __next__ (self) : if self.index == len(self.names) - 1 : raise StopIteration else : self.index += 1 return self.names[self.index] def __gt__ (self, other) : print("__gt__被执行" , f"战斗力大于60的复仇者:{other} " ) members = filter(lambda x: x[-1 ] > other, self.members.items()) return list(members) def __len__ (self) : return len(self.members) def __str__ (self) : power_info = map(lambda x: "{0}的战斗力为 {1}" .format(*x), self.members.items()) return "\n" .join(power_info) if __name__ == '__main__' : a = Avenger(钢铁侠=106 , 雷神=110 , 浩克=120 , 奇异博士=90 , 蜘蛛侠=80 , 美国队长=50 , 幻世=60 ) print(len(a)) print(f"======== 雷神的战斗力:{a['雷神' ]} ========" ) a["古鲁姆" ] = 15.6 del a["幻世" ] for i in a: print(i) print(a > 60 )
结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 7 __getitem__被执行: 正在读取复仇者【雷神】的信息,其战斗力为110 ======== 雷神的战斗力:110 ======== __setitem__被执行: 新增复仇者【古鲁姆】,其战斗力为15.6 __delitem__被执行: 复仇者【幻世】已阵亡! __iter__被执行 雷神 浩克 奇异博士 蜘蛛侠 美国队长 幻世 __gt__被执行 战斗力大于60的复仇者:60 [('钢铁侠' , 106), ('雷神' , 110), ('浩克' , 120), ('奇异博士' , 90), ('蜘蛛侠' , 80)]