# 魔术方法简介
Python 中的魔术方法(Magic)方法,是那些被__
包围的方法,在对象继承时,子类可以重写父类的魔术方法以实现定制功能,用于增强Python面向对象编程的能力。魔术方法在创建对象或对象操作时自动调用,不需要显式使用。譬如当我们判断对象是否相等时只使用了==
符号,并未显式调用__eq__
方法,但却实现了判断两个实例是否相等的功能,犹如变魔法一般。魔术方法按功能可分为如下几类,
- 1.构造、初始化和析构对象
- 2.控制属性读写
- 3.对象的表示和描述,如
__str__
等 - 4.在定制类对象间支持使用运算符号,如
+
,-
,*
,/
,//
,==
,<=
等 - 5.定义个性化序列
- 6.反射
- 7.可调用对象
- 8.上下文管理
- 9.描述器
- 10.复制
- 11.使对象支持
pickle
序列化反序列化
因不同的PEP(Python Enhancement Proposal)
会不断添加删除魔术方法,故本文具有一定时效性。本文中涉及的魔术方法列表:
序号 | 方法 | 序号 | 方法 |
---|---|---|---|
1 | __init__ | 42 | __radd__ |
2 | __new__ | 43 | __iadd__ |
3 | __del__ | 44 | __int__ |
4 | __getattr__ | 45 | __float__ |
5 | __getattribute__ | 46 | __oct__ |
6 | __setattr__ | 47 | __hex__ |
7 | __delattr__ | 48 | __index__ |
8 | __str__ | 49 | __complex__ |
9 | __repr__ | 50 | __len__ |
10 | __format__ | 51 | __getitem__ |
11 | __hash__ | 52 | __setitem__ |
12 | __bool__ | 53 | __delitem__ |
13 | __dir__ | 54 | __iter__ |
14 | __sizeof__ | 55 | __reversed__ |
15 | __eq__ | 56 | __contains__ |
16 | __ne__ | 57 | __missing__ |
17 | __le__ | 58 | __instancecheck__ |
18 | __ge__ | 59 | __subclasscheck__ |
19 | __lt__ | 60 | __call__ |
20 | __gt__ | 61 | __enter__ |
21 | __pos__ | 62 | __exit__ |
22 | __neg__ | 63 | __get__ |
23 | __invert__ | 64 | __set__ |
24 | __round__ | 65 | __delete__ |
25 | __floor__ | 66 | __copy__ |
26 | __ceil__ | 67 | __deepcopy__ |
27 | __trunc__ | 68 | __getstate__ |
28 | __add__ | 69 | __setstate__ |
29 | __sub__ | 70 | __slots__ |
30 | __mul__ | ||
31 | __matmul__ | ||
32 | __truediv__ | ||
33 | __floordiv__ | ||
34 | __mod__ | ||
35 | __divmod__ | ||
36 | __pow__ | ||
37 | __lshift__ | ||
38 | __rshift__ | ||
39 | __and__ | ||
40 | __or__ | ||
41 | __xor__ |
# 用于对象构造析构的魔术方法
__init__
:在定义class
时使用最多的方法,用于初始化对象, 在创建对象时自动调用。__new__
:初始化对象时,在__init__
之前调用的方法,该方法实现对象的构造,返回对象实例给__init__
中的self
。__del__
:__del__
方法是定义类的析构函数,其并不是为了定义del Object
时对象的行为,而是为了在对象被垃圾回收时执行特定的操作,如关闭socket
,文件描述符等。因为当Python
解释器退出时,对象有可能依然未被释放,因此__del__
中定义册操作不一定能被执行,故在实际中应避免使用__del__
。
示例:
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.
"""
# 用于控制属性读写的魔术方法
__getattr__
:拦截通过obj.key
获取对象不存在的key
属性时调用的方法,注意与__getattribute__
方法的区别,可实现懒加载,在调用属性时再去操作耗时的动作,如文件读取等。__getattribute__
:拦截所有的属性访问,注意避免在此方法中使用self.key
,以免造成死循环__setattr__
:拦截所有属性的赋值操作,注意避免在此方法中通过self.key=value
的方式给实例赋值,以免造成死循环。__delattr__
:拦截所有属性的清除操作,注意避免在此方法中通过del self.key
的方式清除实例属性,以免造成死循环。
示例:
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__
"""
# 对象的表示和描述
__str__
:使得可通过str()
方法获取对象的可读信息,__str__输出的应该是用户关注的容易理解的信息,因此对那些负责与客户交互的类,至少更应该重写__str__方法。__repr__
:使得可通过repr()
方法获取对象的详细信息,包括存储地址等,__str__
中的信息是__repr__
中的一部分。stackoverflow
上有个回答。1 (opens new window),__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
类时比较有用。
示例:
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[code].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))
# 在定制类对象间支持使用运算符号
# 支持比较运算符号
__eq__(self, obj)
:支持==
__ne__(self, obj)
:支持==
__le__(self, obj)
:支持<=
__ge__(self, obj)
:支持>=
__lt__(self, obj)
:支持<
__gt__(self, obj)
:支持>
示例:
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)
# 支持一元运算符
__pos__
:支持+
,大多数情况下+x
依然是x
,一元运算符+
没有执行任何操作,如+(-1)
还是-1
,在__pos__
中可以定义+
的操作。__neg__
:支持-
运算符__invert__
:~
符号的逻辑__abs__
:定制abs()
方法调用时的逻辑__round__
:round()
方法调用时的逻辑__floor__
:定制math.floor()
方法调用时的逻辑,譬如向下取整__ceil__
:定制math.ceil()
方法调用时的逻辑,譬如向上取整__trunc__
:定制math.trunc()
方法调用时的逻辑
示例
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)
# 支持算术运算符
__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__
:支持^
,按位异或
示例:
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.3
中的是常规算术运算符,a+b
或者b+a
都可以进行运算,反射运算符指如a+b
时,a
中没有定义需调用b
的运算方法实现运算的运算符。如__radd__
,则要执行此方法a
中需没有实现__add__
和__radd__
方法,才会去调用b
中对应的r方法
3 (opens new window)
示例
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)
# 自增算术运算符
自增运算符:执行相应运算,并将结果赋予左值,如a+=b
。
示例
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)
# 支持类型转换运算符
__int__
:支持int()
__float__
:支持float()
__oct__
:支持oct()
__hex__
:支持hex()
__index__
:返回1个整数,支持作为list
下标__complex__
:支持complex()
函数
示例
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))
# 定制个性化序列
定义一个不可变容器,只需要实现__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
没有定义时调用。
示例:
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)
# 反射,实例类型判断
__instancecheck__
:支持instancecheck
方法__subclasscheck__
:支持subclasscheck
方法,此两个方法需定义在元类中,否则调用的还是type
中的方法4 (opens new window)
示例
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))
# 可调用对象
__call__
:对象像函数一样可调用
示例
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))
# 上下文管理
python
中使用with
语句执行上下文管理,处理内存块被创建时和内存块执行结束时上下文应该执行的操作,上下文即程序执行操作的环境,包括异常处理,文件开闭。有两个魔术方法负责处理上下文管理。
__enter__
:上下文创建时执行的操作,该方法返回as
后面的对象。__exit__
:上下文结束时执行的操作
示例
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")
# 描述器
描述器让对象能够自定义属性查找、存储和删除的操作。描述器是任何一个定义了
__get__()
,__set__()
或__delete__()
的对象。描述器的主要目的是提供一个挂 钩,允许存储在类变量中的对象控制在属性查找期间发生的情况。5 (opens new window)。 类似于Java
类中的getters\setters
6 (opens new window)。常用于动态查找,属性托管,定制名称,自定义验证等。__get__
:查找属性时调用__set__
:给属性赋值时调用__delete__
:移除属性时调用
示例:
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
# 复制对象
__copy__
:执行copy.copy()
时调用__deepcopy__
:执行copy.deepcopy()
时调用,见7 (opens new window)
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
# Pickle序列化对象
pickle
是一种Python特有的自描述的数据编码。 通过自描述,被序列化后的数据包含每个对象 开始和结束以及它的类型信息。8 (opens new window)。些类型的对象是不能被序列化的。这些通常是那些依赖外部系统状态的对象, 比如打开的文 件,网络连接,线程,进程,栈帧等等。 用户自定义类可以通过提供 __getstate__()
和 __setstate__()
方法来绕过这些限制。
__getstate__
:序列化对象时调用__setstate__
:反序列化时被调用
示例:
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 (opens new window)
示例:
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 (opens new window)
- 2.https://zhuanlan.zhihu.com/p/37643853 (opens new window)
- 3.https://docs.python.org/3/reference/datamodel.html (opens new window)
- 4.https://www.cnblogs.com/traditional/p/11731676.html (opens new window)
- 5.https://docs.python.org/zh-cn/3/howto/descriptor.html#closing-thoughts (opens new window)
- 6.https://stackoverflow.com/questions/3798835/ (opens new window)
- 7.https://stackoverflow.com/questions/1500718/how-to-override-the-copy-deepcopy-operations-for-a-python-object (opens new window)
- 8.https://python3-cookbook.readthedocs.io/zh_CN/latest/c05/p21_serializing_python_objects.html (opens new window)
- 9.https://rszalski.github.io/magicmethods/#sequence (opens new window)
- 10.https://docs.python.org/3/reference/datamodel.html (opens new window)