1.魔术方法简介
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__ |
2.不同类型的魔术方法
2.1 用于对象构造析构的魔术方法
__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.
"""
2.2 用于控制属性读写的魔术方法
__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__
"""
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类时比较有用。
示例:
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)
Python魔术方法详解:构建自定义对象行为

本文深入探讨了Python中的魔术方法,包括构造、属性访问、表示和描述、运算符重载等方面,展示了如何通过魔术方法定制类的行为,增强面向对象编程能力。通过实例演示了如何使用魔术方法实现对象的初始化、属性读写、比较运算、类型转换等操作,以及上下文管理、序列化和复制等高级特性。
最低0.47元/天 解锁文章
626





