# 魔术方法简介

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 setTrue,则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\setters6 (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

# 参考资料

(adsbygoogle = window.adsbygoogle || []).push({});