Python中的魔术方法
Catalogue
1.魔术方法简介
Python 中的魔术方法(Magic)方法,是那些被__
包围的方法,在对象继承时,子类可以重写父类的魔术方法以实现定制功能,用于增强Python面向对象编程的能力。魔术方法在创建对象或对象操作时自动调用,不需要显式使用。譬如当我们判断对象是否相等时只使用了==
符号,并未显式调用__eq__
方法,但却实现了判断两个实例是否相等的功能,犹如变魔法一般。魔术方法按功能可分为如下几类,
- 1.构造、初始化和析构对象
- 2.控制属性读写
- 3.对象的表示和描述,如
__str__
等 - 4.在定制类对象间支持使用运算符号,如
+
,-
,*
,/
,//
,==
,<=
等 - 5.定义个性化序列
- 6.反射
- 7.可调用对象
- 8.上下文管理
- 9.描述器
- 10.复制
- 11.使对象支持
pickle
序列化反序列化
因不同的PEP(Python Enhancement Proposal)
会不断添加删除魔术方法,故本文具有一定时效性。本文中涉及的魔术方法列表:
python的魔术方法大全
在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”(魔术方法),例如类的初始化方法 __init__ ,Python中所有的魔术方法均在官方文档中有相应描述,这边给大家把所有的魔术方法汇总了一下,希望对大家的学习有所帮助。
一、基本的魔法方法
方法 | 说明 |
---|---|
__new__(cls[, ...]) |
创建对象的方法,当通过类去创建对象时就是调用__new__ 方法去创建的 |
__init__(self[, ...]) |
初始化方法,当实例被创建的时候调用的初始化方法 |
__del__(self) |
析构方法,当实例被销毁的时候调用的方法 |
__call__(self[, args...]) |
允许一个类的实例像函数一样被调用:x(a, b) 调用 x.__call__ (a, b) |
__repr__(self) |
定义当被 repr() 调用时的行为 |
__str__(self) |
定义当被 str() 调用时的行为 |
__bytes__(self) |
定义当被 bytes() 调用时的行为 |
__hash__(self) |
定义当被 hash() 调用时的行为 |
__bool__(self) |
定义当被 bool() 调用时的行为,应该返回 True 或 False |
__format__(self, format_spec) |
定义当被 format() 调用时的行为 |
二、属性相关的魔法方法
方法 | 说明 |
---|---|
__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) |
定义当描述符的值被删除时的行为 |
三、上下文管理器
方法 | 说明 |
---|---|
__enter__ (self) |
定义当使用 with 语句进入上下文管理器执行的方法 ,__enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定 |
__exit__ (self, exc_type, exc_value, traceback) |
当with中的代码块执行完毕后调用的方法。一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作 |
四、容器类型数据相关
方法 | 说明 |
---|---|
__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)时的行为 |
五、比较运算符
方法 | 说明 |
---|---|
__lt__(self, other) |
定义小于号的行为:x < y 调用 x.__lt__ (y) |
__le__(self, other) |
定义小于等于号的行为:x <= y 调用 x.__le__ (y) |
__eq__(self, other) |
定义等于号的行为:x == y 调用 x.__eq__ (y) |
__ne__(self, other) |
定义不等号的行为:x != y 调用 x.__ne__ (y) |
__gt__(self, other) |
定义大于号的行为:x > y 调用 x.__gt__ (y) |
__ge__(self, other) |
定义大于等于号的行为:x >= y 调用 x.__ge__ (y) |
六、算术运算符
方法 | 说明 |
---|---|
__add__(self, other) |
定义加法的行为:+ |
__sub__(self, other) |
定义减法的行为:- |
__mul__(self, other) |
定义乘法的行为:* |
__truediv__(self, other) |
定义真除法的行为:/ |
__floordiv__(self, other) |
定义整数除法的行为:// |
__mod__(self, other) |
定义取模算法的行为:% |
__divmod__(self, other) |
定义当被 divmod() 调用时的行为 |
__pow__(self, other[, modulo]) |
定义当被 power() 调用或 ` 运算时的行为 |
__lshift__(self, other) |
定义按位左移位的行为:<< |
__rshift__(self, other) |
定义按位右移位的行为:>> |
__and__(self, other) |
定义按位与操作的行为:& |
__xor__(self, other) |
定义按位异或操作的行为:^ |
__or__(self, other) |
定义按位或操作的行为:| |
七、反运算
方法 | 说明 |
---|---|
__radd__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rsub__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rmul__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rtruediv__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rfloordiv__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rmod__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rdivmod__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rpow__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rlshift__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rrshift__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rand__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__rxor__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
__ror__(self, other) |
(与上方相同,当左操作数不支持相应的操作时被调用) |
八、增量赋值运算
方法 | 说明 |
---|---|
__iadd__(self, other) |
定义赋值加法的行为:+= |
__isub__(self, other) |
定义赋值减法的行为:-= |
__imul__(self, other) |
定义赋值乘法的行为:*= |
__itruediv__(self, other) |
定义赋值真除法的行为:/= |
__ifloordiv__(self, other) |
定义赋值整数除法的行为://= |
__imod__(self, other) |
定义赋值取模算法的行为:%= |
__ipow__(self, other[, modulo]) |
定义赋值幂运算的行为:`= |
__ilshift__(self, other) |
定义赋值按位左移位的行为:<<= |
__irshift__(self, other) |
定义赋值按位右移位的行为:>>= |
__iand__(self, other) |
定义赋值按位与操作的行为:&= |
__ixor__(self, other) |
定义赋值按位异或操作的行为:^= |
__ior__(self, other) |
定义赋值按位或操作的行为:|= |
九、一元操作符
方法 | 说明 |
---|---|
__pos__(self) |
定义正号的行为:+x |
__neg__(self) |
定义负号的行为:-x |
__abs__(self) |
定义当被 abs() 调用时的行为 |
__invert__(self) |
定义按位求反的行为:~x |
十、类型转换
方法 | 说明 |
---|---|
__complex__(self) |
定义当被 complex() 调用时的行为(需要返回恰当的值) |
__int__(self) |
定义当被 int() 调用时的行为(需要返回恰当的值) |
__float__(self) |
定义当被 float() 调用时的行为(需要返回恰当的值) |
__round__(self) |
定义当被 round() 调用时的行为(需要返回恰当的值) |
__index__(self) |
1. 当对象是被应用在切片表达式中时,实现整形强制转换 2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 __index__ 3. 如果 __index__ 被定义,则 int 也需要被定义,且返回相同的值 |
2.不同类型的魔术方法
2.1 用于对象构造析构的魔术方法
__init__
:在定义class
时使用最多的方法,用于初始化对象, 在创建对象时自动调用。__new__
:初始化对象时,在__init__
之前调用的方法,该方法实现对象的构造,返回对象实例给__init__
中的self
。__del__
:__del__
方法是定义类的析构函数,其并不是为了定义del Object
时对象的行为,而是为了在对象被垃圾回收时执行特定的操作,如关闭socket
,文件描述符等。因为当Python
解释器退出时,对象有可能依然未被释放,因此__del__
中定义册操作不一定能被执行,故在实际中应避免使用__del__
。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class World: def __new__(cls, *args, **kargs): print("__new__ method called.") inst = super(World, cls).__new__(cls) return inst def __init__(self, countries): print("__init__ method called.") self.countrie = countries def __del__(self): print("Your World Is Cleaned Up.") w = World(1) o = World(2) del o """ Output: # __new__ method called. # __init__ method called. # __new__ method called. # __init__ method called. # Your World Is Cleaned Up. """ |
2.2 用于控制属性读写的魔术方法
__getattr__
:拦截通过obj.key
获取对象不存在的key
属性时调用的方法,注意与__getattribute__
方法的区别,可实现懒加载,在调用属性时再去操作耗时的动作,如文件读取等。__getattribute__
:拦截所有的属性访问,注意避免在此方法中使用self.key
,以免造成死循环__setattr__
:拦截所有属性的赋值操作,注意避免在此方法中通过self.key=value
的方式给实例赋值,以免造成死循环。__delattr__
:拦截所有属性的清除操作,注意避免在此方法中通过del self.key
的方式清除实例属性,以免造成死循环。
示例:
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 |
class World: def __init__(self, countries): self.countries = countries def __getattr__(self, key): print(f"__getattr__ called: unexisted key {key}") return None def __getattribute__(self, key): print(f"__getattribute__ called: key {key}") return super(World, self).__getattribute__(key) def __setattr__(self, key, value): if key in self.__dict__: print(f"__setattr__ called: key existed {key}") else: print(f"__setattr__ called: key unexisted {key}") self.__dict__[key] = value def __delattr__(self, key): print(f"__delattr__ called: key {key}") del self.__dict__[key] w = World(256) w.oceans = 5 del w.countries """ Output: # __getattribute__ called: key __dict__ #__setattr__ called: key unexisted countries # __getattribute__ called: key __dict__ # __getattribute__ called: key __dict__ # __setattr__ called: key unexisted oceans # __getattribute__ called: key __dict__ # __delattr__ called: key countries # __getattribute__ called: key __dict__ """ |
2.3 对象的表示和描述
__str__
:使得可通过str()
方法获取对象的可读信息,__str__输出的应该是用户关注的容易理解的信息,因此对那些负责与客户交互的类,至少更应该重写__str__方法。__repr__
:使得可通过repr()
方法获取对象的详细信息,包括存储地址等,__str__
中的信息是__repr__
中的一部分。stackoverflow
上有个回答。1,__str__
是面向普通用户的信息,__repr__
是面向开发者的信息,倒也形象。如果开发者要输出开发人员足够知悉的属性,就需要重写该方法。 重写__repr__方法注意:__repr__方法是实例方法,因此带一个参数self,也只能带这个参数; 输出的信息尽可能满足开发者的要求,信息必须详尽和准确。__format__
:定义通过"prefix {:code}".format(obj)时实例输出的形式,code
是__format__
的参数,用来确定以哪种格式format
,定制数值或字符串类型时常希望重写此方法__hash__
:定义hash
对象实例的返回值,需要为1个整数,默认的hash
方法根据对象的地址和属性值算出来int类型整数为对象的hash
值。一个对象在其生命周期内,如果保持不变,就是hashable
(可哈希的)。像int
,string
是可哈希的,dict
,list
等是不可哈希的。哈希性使得对象可以用作dictionary
键和set
成员,因为这些数据结构在内部使用了哈希值。如果要判断item in set
为True
,则item==set中的一个元素
且hash(item)==hash(set中的一个元素)
。__bool__
:调用bool()
方法时对象的返回值,需为True/False
。__dir__
:调用dir()
方法时对象的返回值,一般不需要重写该方法,但在定义__getattr__
时有可能需要重写,以打印动态添加的属性。__sizeof__
:定义sys.getsizeof()
方法调用时的返回,指类的实例的大小,单位是字节,在使用C
扩展编写Python
类时比较有用。
示例:
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 |
formats = { "long":"Country Has {c.provinces} Provinces", "short":"C H {c.provinces} P", } class Country: def __init__(self, provinces, name): self.provinces = provinces self.name = name def __str__(self): return f"Country has {self.provinces} provinces" def __repr__(self): s="In __repr__:\n<{} object at {:#016x}>\n".format(repr(self.__class__),id(self) ) s+=super().__repr__() s+="\n" s+=repr(self.__dict__) return s def __format__(self, code): return formats .format(c=self) def __eq__(self, obj): return self.provinces == obj.provinces def __hash__(self): return 12 #return hash(self.name) def __bool__(self): print(self.name) return self.name == "Hunan" def __dir__(self): l = list(self.__dict__.keys()) l.append("GDP") return l def __sizeof__(self): print("__sizeof__ called") return len(self.__dict__) c = Country(264, "Hunan") d = Country(264, "Henan") print(hash(c)) print(hash(d)) print(c == d) s = set() s.add(c) print(d in s) print(bool(c), bool(d)) import sys print(sys.getsizeof(c)) |
2.4 在定制类对象间支持使用运算符号
2.4.1支持比较运算符号
__eq__(self, obj)
:支持==
__ne__(self, obj)
:支持!=
__le__(self, obj)
:支持<=
__ge__(self, obj)
:支持>=
__lt__(self, obj)
:支持<
__gt__(self, obj)
:支持>
示例:
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 Point3D: def __init__(self, x, y, z): self.x = x self.y = y self.z = z def __square_sum(self, obj:Point3D): return sum([pow(getattr(obj, k), 2) for k in obj.__dict__]) def __eq__(self, obj): print("equal called") d1 = self.__square_sum(self) d2 = self.__square_sum(obj) return d1 == d2 def __ne__(self, obj): print("not equal called") d1 = self.__square_sum(self) d2 = self.__square_sum(obj) return d1 != d2 def __le__(self, obj): print("not equal called") d1 = self.__square_sum(self) d2 = self.__square_sum(obj) return d1 <= d2 def __ge__(self, obj): print("not equal called") d1 = self.__square_sum(self) d2 = self.__square_sum(obj) return d1 >= d2 def __lt__(self, obj): print("less than called") d1 = self.__square_sum(self) d2 = self.__square_sum(obj) return d1 < d2 def __gt__(self, obj): print("great than called") d1 = self.__square_sum(self) d2 = self.__square_sum(obj) return d1 > d2 p1 = Point3D(1,2,3) p2 = Point3D(2,2,3) print(p1 == p2) print(p1 != p2) print(p1 <= p2) print(p1 >= p2) print(p1 > p2) print(p1 < p2) |
2.4.2支持一元运算符
__pos__
:支持+
,大多数情况下+x
依然是x
,一元运算符+
没有执行任何操作,如+(-1)
还是-1
,在__pos__
中可以定义+
的操作。__neg__
:支持-
运算符__invert__
:~
符号的逻辑__abs__
:定制abs()
方法调用时的逻辑__round__
:round()
方法调用时的逻辑__floor__
:定制math.floor()
方法调用时的逻辑,譬如向下取整__ceil__
:定制math.ceil()
方法调用时的逻辑,譬如向上取整__trunc__
:定制math.trunc()
方法调用时的逻辑
示例
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 |
class MyNum(int): def __init__(self, x): self.x = x def __pos__(self): return abs(self.x) def __neg__(self): return -self.x def __abs__(self): return pow(self.x,2) def __invert__(self): return -1 if self.x > 0 else 1 def __round__(self, n): print(f"{n} digits after point would be remained.") return 1 if self.x > 0 else 0 def __floor__(self): return self.x - 1 def __ceil__(self): return self.x + 1 def __trunc__(self): return self.x // 10 a = MyNum(-12) print(abs(a)) print(+a) print(~a) print(-a) round(a, 10) |
2.4.3支持算术运算符
__add__
: 支持+
__sub__
:支持-
__mul__
:支持*
__matmul__
:支持@
,矩阵乘法,Python3.5
后引入的新特性,支持numpy
数组表示的矩阵乘法__truediv__
:支持/
,__div__
是Python2中的属性
,Python3
中用__truediv__
__floordiv__
: 支持//
__mod__
:支持%
,取余__divmod__
:支持divmod()
方法,返回tuple(x//y,x%y)
__pow__
:支持**
__lshift__
:支持左移位<<
__rshift__
:支持右移位>>
__and__
:支持&
,按位与__or__
:支持|
,按位或__xor__
:支持^
,按位异或
示例:
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
class Point2D: def __init__(self, x, y): self.x = x self.y = y def __add__(self, obj): print("__add__ called") return (self.x + obj.x, self.y + obj.y) def __sub__(self, obj): print("__sub__ called") return (self.x - obj.x, self.y - obj.y) def __mul__(self, obj): print("__mul__ called") return (self.x * obj.x, self.y * obj.y) def __matmul__(self, obj): import numpy as np print("__matmul__ called") return np.array([self.x, obj.x]) @ np.array([self.y, obj.y]) def __floordiv__(self, obj): print("__floordiv__ called") return (self.x // obj.x, self.y // obj.y) def __truediv__(self, obj): print("__div__ called") return (self.x / obj.x, self.y / obj.y) def __mod__(self, obj): print("__mod__ called") return (self.x % obj.x, self.y % obj.y) def __divmod__(self, obj): print("__divmod__ called") return (divmod(self.x, obj.x), divmod(self.y, obj.y)) def __pow__(self, obj): print("__pow__ called") return (self.x ** obj.x, self.y ** obj.y) def __lshift__(self, obj): print("__lshift__ called") return (self.x << obj.x, self.y << obj.y) def __rshift__(self, obj): print("__rshift__ called") return (self.x >> obj.x, self.y >> obj.y) def __and__(self, obj): print("__and__ called") return (self.x & obj.x, self.y & obj.y) def __or__(self, obj): print("__or__ called") return (self.x | obj.x, self.y | obj.y) def __xor__(self, obj): print("__xor__ called") return (self.x ^ obj.x, self.y ^ obj.y) p1 = Point2D(8, 16) p2 = Point2D(1, 3) print(p1 + p2) print(p1 - p2) print(p1 * p2) print(p1 @ p2) print(p1 / p2) print(p1 // p2) print(p1 % p2) print(divmod(p1, p2)) print(p1 ** p2) print(p1 << p2) print(p1 >> p2) print(p1 & p2) print(p1 | p2) print(p1 ^ p2) |
2.4.4反射算术运算符
2.4.3
中的是常规算术运算符,a+b
或者b+a
都可以进行运算,反射运算符指如a+b
时,a
中没有定义需调用b
的运算方法实现运算的运算符。如__radd__
,则要执行此方法a
中需没有实现__add__
和__radd__
方法,才会去调用b
中对应的r方法
3
示例
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
class Point2DA: def __init__(self, x, y): self.x = x self.y = y class Point2DB: def __init__(self, x, y): self.x = x self.y = y def __radd__(self, obj): print("__radd__ called") return (self.x + obj.x, self.y + obj.y) def __rsub__(self, obj): print("__rsub__ called") return (self.x - obj.x, self.y - obj.y) def __rmul__(self, obj): print("__rmul__ called") return (self.x * obj.x, self.y * obj.y) def __rmatmul__(self, obj): import numpy as np print("__rmatmul__ called") return np.array([self.x, obj.x]) @ np.array([self.y, obj.y]) def __rfloordiv__(self, obj): print("__rfloordiv__ called") return (self.x // obj.x, self.y // obj.y) def __rtruediv__(self, obj): print("__rtruediv__ called") return (self.x / obj.x, self.y / obj.y) def __rmod__(self, obj): print("__rmod__ called") return (self.x % obj.x, self.y % obj.y) def __rpow__(self, obj): print("__rpow__ called") return (self.x ** obj.x, self.y ** obj.y) def __rlshift__(self, obj): print("__rlshift__ called") return (self.x << obj.x, self.y << obj.y) def __rrshift__(self, obj): print("__rrshift__ called") return (self.x >> obj.x, self.y >> obj.y) def __rand__(self, obj): print("__rand__ called") return (self.x & obj.x, self.y & obj.y) def __ror__(self, obj): print("__ror__ called") return (self.x | obj.x, self.y | obj.y) def __rxor__(self, obj): print("__rxor__ called") return (self.x ^ obj.x, self.y ^ obj.y) p1 = Point2DA(3,4) p2 = Point2DB(2,3) print(p1 + p2) print(p1 - p2) print(p1 * p2) print(p1 @ p2) print(p1 / p2) print(p1 // p2) print(p1 % p2) print(p1 ** p2) print(p1 << p2) print(p1 >> p2) print(p1 & p2) print(p1 | p2) print(p1 ^ p2) |
2.4.5自增算术运算符
自增运算符:执行相应运算,并将结果赋予左值,如a+=b
。
示例
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
class Point2D: def __init__(self, x, y): self.x = x self.y = y def __iadd__(self, other): print("__iadd__ called") self.x += other.x self.y += other.y return self def __isub__(self, other): print("__isub__ called") self.x -= other.x self.y -= other.y return self def __imul__(self, other): print("__imul__ called") self.x *= other.x self.y *= other.y return self def __imatmul__(self, other): print("__imatmul__ called") import numpy as np v = np.array([self.x, other.x]) @ np.array([self.y, other.y]) self.x = v self.y = v return self def __itruediv__(self, other): print("__itruediv__ called") self.x /= other.x self.y /= other.y return self def __ifloordiv__(self, other): print("__ifloordiv__ called") self.x //= other.x self.y //= other.y return self def __imod__(self, other): print("__imod__ called") self.x %= other.x self.y %= other.y return self def __ipow__(self, other): print("__ipow__ called") self.x **= other.x self.y **= other.y return self def __ilshift__(self, other): print("__ilshift__ called") self.x <<= other.x self.y <<= other.y return self def __irshift__(self, other): print("__irshift__ called") self.x >>= other.x self.y >>= other.y return self def __iand__(self, other): print("__iand__ called") self.x &= other.x self.y &= other.y return self def __ixor__(self, other): print("__ixor__ called") self.x |= other.x self.y |= other.y return self def __ior__(self, other): print("__ior__ called") self.x ^= other.x self.y ^= other.y return self p1 = Point2D(2,3) p2 = Point2D(3,4) p1 += p2 print(p1.x, p1.y) p1 -= p2 print(p1.x, p1.y) p1 *= p2 print(p1.x, p1.y) p1 @= p2 print(p1.x, p1.y) p1 /= p2 print(p1.x, p1.y) p1 = p2 print(p1.x, p1.y) p1 //= p2 print(p1.x, p1.y) p1 %= p2 print(p1.x, p1.y) p1 **= p2 print(p1.x, p1.y) p1 <<= p2 print(p1.x, p1.y) p1 >>= p2 print(p1.x, p1.y) p1 &= p2 print(p1.x, p1.y) p1 |= p2 print(p1.x, p1.y) p1 ^= p2 print(p1.x, p1.y) |
2.4.6支持类型转换运算符
__int__
:支持int()
__float__
:支持float()
__oct__
:支持oct()
__hex__
:支持hex()
__index__
:返回1个整数,支持作为list
下标__complex__
:支持complex()
函数
示例
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 |
class MyNum(int): def __init__(self, x): self.x = x def __int__(self): print("__int__ called") return int(self.x) def __float__(self): print("__float__ called") return float(self.x) def __oct__(self): print("__oct__ called") return oct(self.x) def __hex__(self): print("__hex__ called") return hex(self.x) def __index__(self): print("__index__ called") return int(self.x + 1) def __complex__(self): print("__complex__ called") return complex(self.x, self.x) n = MyNum(1.2) m = MyNum(2) a = [1,2,3] print(int(n)) print(float(n)) print(oct(n)) print(hex(n)) print(a[n]) print(complex(n)) |
2.5定制个性化序列
定义一个不可变容器,只需要实现__len__
和__getitem__
,可变容器在此基础上还需实现__setitem__
和__delitem__
,如果希望容器是可迭代的,还需实现__iter__
方法。
__len__
:返回序列的长度,支持len()
__getitem__
:支持self[key]
__setitem__
:支持self[key]=value
__delitem__
:支持del self[key]
__iter__
:支持for item in self
__reversed__
:支持reversed()
方法__contains__
:支持in
判断__missing__
:当定制序列是dict
的子类时,使用dict[key]
读取元素而key
没有定义时调用。
示例:
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
class FunctionalList: def __init__(self, values=None): if values is None: self.values = [] else: self.values = values def __len__(self): return len(self.values) def __getitem__(self, key): # if key is of invalid type or value, the list values will raise the error return self.values[key] def __setitem__(self, key, value): self.values[key] = value def __delitem__(self, key): del self.values[key] def __iter__(self): return iter(self.values) def __reversed__(self): return reversed(self.values) def __contains__(self, val): print("__contains__ called") return True if val in self.values else False def append(self, value): self.values.append(value) def head(self): # get the first element return self.values[0] def tail(self): # get all elements after the first return self.values[1:] def init(self): # get elements up to the last return self.values[:-1] def last(self): # get last element return self.values[-1] def drop(self, n): # get all elements except first n return self.values[n:] def take(self, n): # get first n elements return self.values[:n] l = FunctionalList() l.append(2) l.append(5) l.append(4) print(l) for i in l: print(i) del l[1] print(2 in l) for i in l: print(i) print(l[0]) l = reversed(l) for i in l: print(i) |
2.6 反射,实例类型判断
__instancecheck__
:支持instancecheck
方法__subclasscheck__
:支持subclasscheck
方法,此两个方法需定义在元类中,否则调用的还是type
中的方法4
示例
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 |
class MyType(type): def __instancecheck__(self, instance): print("__instancecheck__ called") if hasattr(instance, "password"): return True # 否则返回False return False class A(metaclass=MyType): def __init__(self, x): super(A, self).__init__() self.x = x a = [] print(isinstance(a, A)) class MyType(type): def __subclasscheck__(self, subclass): print("__subclasscheck__ called.") return hasattr(subclass, "password") class B(metaclass=MyType): pass b = B() print(issubclass(b, B)) b = B() b.password = "12" print(issubclass(b, B)) |
2.7可调用对象
__call__
:对象像函数一样可调用
示例
1 2 3 4 5 6 7 8 9 |
class Entity: def __init__(self, size, x, y): self.x, self.y = x, y self.size = size def __call__(self, s): return self.x*self.y + s e = Entity(2, 2, 3) print(e(3)) |
2.8 上下文管理
python
中使用with
语句执行上下文管理,处理内存块被创建时和内存块执行结束时上下文应该执行的操作,上下文即程序执行操作的环境,包括异常处理,文件开闭。有两个魔术方法负责处理上下文管理。
__enter__
:上下文创建时执行的操作,该方法返回as
后面的对象。__exit__
:上下文结束时执行的操作
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class Closer: '''A context manager to automatically close an object with a close method in a with statement.''' def __init__(self, obj): self.obj = obj def __enter__(self): print('__enter__ called') return self.obj # bound to target def __exit__(self, exception_type, exception_val, trace): print('__exit__ called') try: self.obj.close() except AttributeError: print('Not closable. here') return True with Closer(open("1.txt", "w")) as f: f.write("1") |
2.9描述器
- 描述器让对象能够自定义属性查找、存储和删除的操作。描述器是任何一个定义了
__get__()
,__set__()
或__delete__()
的对象。描述器的主要目的是提供一个挂 钩,允许存储在类变量中的对象控制在属性查找期间发生的情况。5。 类似于Java
类中的getters\setters
6。常用于动态查找,属性托管,定制名称,自定义验证等。 __get__
:查找属性时调用__set__
:给属性赋值时调用__delete__
:移除属性时调用
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import logging logging.basicConfig(level=logging.INFO) class LoggedAgeAccess: def __get__(self, obj, objtype=None): value = obj._age logging.info('Accessing %r giving %r', 'age', value) return value def __set__(self, obj, value): logging.info('Updating %r to %r', 'age', value) obj._age = value class Person: age = LoggedAgeAccess() # Descriptor instance def __init__(self, name, age): self.name = name self.age = age # Regular instance attribute # Calls __set__()__set__() def birthday(self): self.age += 1 |
2.10 复制对象
__copy__
:执行copy.copy()
时调用__deepcopy__
:执行copy.deepcopy()
时调用,见7
1 2 3 4 5 6 7 8 9 |
class Obj: def __init__(self, x): self.x = x def __copy__(self): print("__copy__ called") return self def __deepcopy__(self, item): print("__deepcopy__ called") return self |
2.11 Pickle序列化对象
pickle
是一种Python特有的自描述的数据编码。 通过自描述,被序列化后的数据包含每个对象 开始和结束以及它的类型信息。8。些类型的对象是不能被序列化的。这些通常是那些依赖外部系统状态的对象, 比如打开的文 件,网络连接,线程,进程,栈帧等等。 用户自定义类可以通过提供 __getstate__()
和 __setstate__()
方法来绕过这些限制。
__getstate__
:序列化对象时调用__setstate__
:反序列化时被调用 示例:
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 |
import threading import time class Obj: def __init__(self, n): self.n = n self.thread = threading.Thread(target=self.run) def run(self): while self.n > 0: print(f"current value: {self.n}") self.n - 1 o = Obj(1) # Obj不支持序列化,会报错 # import pickle # d = pickle.dumps(o) # no = pickle.loads(d) # print(no.n) class PickelObj: def __init__(self, n): self.n = n self.n_bak = n self.thread = threading.Thread(target=self.run) self.thread.start() def run(self): while self.n > 0: print(f"current value: {self.n}") self.n -= 1 time.sleep(1) def __getstate__(self): print("__getstate__ called") return self.n_bak def __setstate__(self, n): print("__setstate__ called") self.__init__(n) o = PickelObj(3) time.sleep(4) import pickle d = pickle.dumps(o) no = pickle.loads(d) |
其他:
__slots__
,Python
是动态语言,可以给实例或类动态绑定方法和属性,如果我们想要限制 实例的属性怎么办?比如,只允许对Student实例添加name和age属性。为了达到限制的目的, Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属 性。11
示例:1 2 3 4 5
class Student(object): __slots__ = ('name', 'age') s = Student() s.score = 100 #error
参考资料
- 1.https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr
- 2.https://zhuanlan.zhihu.com/p/37643853
- 3.https://docs.python.org/3/reference/datamodel.html
- 4.https://www.cnblogs.com/traditional/p/11731676.html
- 5.https://docs.python.org/zh-cn/3/howto/descriptor.html#closing-thoughts
- 6.https://stackoverflow.com/questions/3798835/
- 7.https://stackoverflow.com/questions/1500718/how-to-override-the-copy-deepcopy-operations-for-a-python-object
- 8.https://python3-cookbook.readthedocs.io/zh_CN/latest/c05/p21_serializing_python_objects.html
- 9.https://rszalski.github.io/magicmethods/#sequence
- 10.https://docs.python.org/3/reference/datamodel.html