0%

Python之Magic Method魔术方法

这里介绍Python中的Magic Method魔术方法

abstract.png

实践

在Python中的Magic Method魔术方法是类中的特殊方法,其通常使用两个下划线进行包裹来命名(典型地: __init__()方法)。普通方法一般需要我们显式调用,而魔术方法则无需显式调用就可以自动执行。这里我们在MyVector类中实现了一些常用的魔术方法。让我们看看这些魔术方法如何自动的被调用

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
import math

class MyVector:
"""
二维向量
"""

def __init__(self, x=0, y=0):
self.x = x
self.y = y

def __str__(self):
return f"[My Vector] x:{self.x}, y:{self.y}"

def __repr__(self):
# 在 f-string 中,默认使用变量的__str__()方法的结果来插入字符串
# 可添加 !r格式化转换标志,以指示此处入应该使用变量的__repr__()方法的结果来插入
return f"My Vector(x={self.x!r}, y={self.y!r})"

def __abs__(self):
return math.hypot(self.x, self.y)

def __bool__(self):
return self.x!=0 or self.y!=0

def __eq__(self, other):
if not isinstance(other, MyVector):
return False
elif self.x!=other.x and self.y!=other.y:
return False
else:
return True

def __hash__(self):
my_tuple = (self.x, self.y)
return hash(my_tuple)

def __call__(self):
print(f"Hello,This is a Vector (x,y)--->>>({self.x, self.y})")

str/repr方法

  • 调用str()方法 或 使用print直接打印对象时,Python会自动调用自定义对象的__str__()方法
  • 调用repr()方法: Python会自动调用自定义对象的__repr__()方法
1
2
3
4
5
6
7
8
print("---------- str、repr 方法 -----------------")
# 调用str()方法 或 使用print直接打印对象时,Python会自动调用自定义对象的__str__()方法
my_vector1 = MyVector(12, "345")
print("my vector 1: ", my_vector1)
print("my vector 1 str: ", str(my_vector1))

# 调用repr()方法: Python会自动调用自定义对象的__repr__()方法
print("my vector 1 repr: ", repr(my_vector1))

figure 1.png

在交互式终端的环境下,直接输出对象时,同样会调用自定义对象的__repr__()方法

figure 2.png

__str__()方法、__repr__()方法 对比:

  • 相同点:都是使用字符串来表示对象
  • 不同点:前者目标是为了人类阅读,聚焦可读性;而后者通常用于提供对象的准确、详细的字符串表示。故该方法目标是给开发者使用,用于调试、重新创建对象
  • Note:如果一个类没有实现 __str__()方法,那么Python在需要调用它时会用 __repr__()方法作为替代

abs方法

调用abs()方法时,Python会自动调用自定义对象的__abs__()方法

1
2
3
4
5
print("-------------abs 方法-------------------")
# 调用abs()方法时: Python会自动调用自定义对象的__abs__()方法
my_vector8 = MyVector(3,4)
num = abs(my_vector8)
print("my_vector8: ", my_vector8, "abs value:", num)

figure 3.png

bool方法

调用bool()方法 或 当对象被用于条件判断时(例如:if语句中) 时,Python会自动调用自定义对象的__bool__()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
print("--------------- bool 方法 --------------------")
# 调用bool()方法时: Python会自动调用自定义对象的__bool__()方法
my_vector9 = MyVector(0,0)
my_vector10 = MyVector(2,0)
print(f"my_vector9: {my_vector9}, bool: {bool(my_vector9)}")
print(f"my_vector10: {my_vector10}, bool: {bool(my_vector10)}")

print("\n-------------------------------------")
# 当对象被用于条件判断时(例如: if语句中): Python会自动调用自定义对象的__bool__()方法
my_vector11 = MyVector(0,0)
my_vector12 = MyVector(3,9)
if my_vector11 :
print(f"my_vector11: {my_vector11} is True")
else :
print(f"my_vector11: {my_vector11} is False")

if my_vector12 :
print(f"my_vector12: {my_vector12} is True")
else :
print(f"my_vector12: {my_vector12} is False")

figure 4.png

需要注意的是,如果一个类没有实现__bool__()方法,那么Python在需要调用它时会调用__len__()方法来获取对象的长度信息,并根据长度是否为零来确定对象的布尔值;如果该类也没有实现 __len__()方法,则其布尔值默认为True

eq/hash方法

使用==运算符时,Python会自动调用自定义对象的__eq__()方法。如果自定义对象中没有自定义实现__eq__()方法。则会使用默认实现:使用is运算符进行判断

1
2
3
4
5
6
7
8
print("--------------- eq 方法 --------------------")
# 使用==运算符时: Python会自动调用自定义对象的__eq__()方法
# 如果自定义对象中没有自定义实现__eq__()方法。则会使用默认实现:使用is运算符进行判断
tom_vector1 = MyVector(3,9)
tom_vector2 = MyVector(3,9)
tom_vector3 = MyVector(2,4)
print( "tom_vector1 == tom_vector2: ", tom_vector1 == tom_vector2 )
print( "tom_vector1 == tom_vector3", tom_vector1 == tom_vector3 )

figure 5.png

调用hash()方法时,Python会自动调用自定义对象的__hash__()方法。如果自定义对象中没有自定义实现__hash__()方法。则会使用默认实现:hash( id(对象)/16 )

1
2
3
4
5
6
7
8
9
print("--------------- hash 方法--------------------")
# 调用hash()方法时: Python会自动调用自定义对象的__hash__()方法
# 如果自定义对象中没有自定义实现__hash__()方法。则会使用默认实现:hash( id(对象)/16 )
aaron_vector1 = MyVector(2,4)
aaron_vector2 = MyVector(2,4)
aaron_vector3 = MyVector(99,33)
print( "hash(aaron_vector1) -->> ", hash(aaron_vector1) )
print( "hash(aaron_vector2) -->> ", hash(aaron_vector2) )
print( "hash(aaron_vector3) -->> ", hash(aaron_vector3) )

figure 6.png

自定义对象如果需要期望可哈希,需要同时正确实现__eq__()、__hash__()两个方法。具体地:

  1. 如果两个对象相等,即__eq__()方法结果为True。则这两个对象的hash值结果必须相同,即__hash__()方法的结果
  2. 如果两个对象的hash值结果相同,即__hash__()方法的结果。则这两个对象不一定会相等,即__eq__()方法结果可能为True 或 False
1
2
3
4
5
6
7
8
9
10
11
12
print("--------------- 对象哈希 --------------------")
aaron_vector1 = MyVector(2,4)
aaron_vector2 = MyVector(2,4)
aaron_vector3 = MyVector(99,33)

# 自定义对象如果需要期望可哈希,需要同时正确实现__eq__()、__hash__()两个方法。具体地:
# 1. 如果两个对象相等,即__eq__()方法结果为True。则这两个对象的hash值结果必须相同,即__hash__()方法的结果
# 2. 如果两个对象的hash值结果相同,即__hash__()方法的结果。则这两个对象不一定会相等,即__eq__()方法结果可能为True 或 False
my_set = {aaron_vector1, aaron_vector2, aaron_vector3}
my_dict = {aaron_vector1: "aaron_1", aaron_vector2:"aaron_2", aaron_vector3: "aaron_3"}
print( f"my set -->> {my_set}")
print( f"my dict -->> {my_dict}")

figure 7.png

直接调用实例

实现__call__()方法,可以让类的实例成为可调用对象。此时直接调用实例,即在实例后添加圆括号,Python会自动调用自定义对象的__call__()方法

1
2
3
4
5
6
print("---------- 直接调用实例 -----------------")
bob_vector = MyVector(-12345,6789)
# 实现__call__()方法,可以让类的实例成为可调用对象
print(f"bob_vector 是否为可调用对象: {callable(bob_vector)}")
# 直接调用实例时: Python会自动调用自定义对象的__call__()方法
bob_vector()

figure 8.png

参考文献

  1. Python编程·第3版:从入门到实践 Eric Matthes著
  2. Python基础教程·第3版 Magnus Lie Hetland著
  3. 流畅的Python·第1版 Luciano Ramalho著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝