这里介绍Python中的Magic Method魔术方法
实践
在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): 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 方法 -----------------")
my_vector1 = MyVector(12, "345") print("my vector 1: ", my_vector1) print("my vector 1 str: ", str(my_vector1))
print("my vector 1 repr: ", repr(my_vector1))
|
在交互式终端的环境下,直接输出对象时,同样会调用自定义对象的__repr__()方法
__str__()方法、__repr__()方法 对比:
- 相同点:都是使用字符串来表示对象
- 不同点:前者目标是为了人类阅读,聚焦可读性;而后者通常用于提供对象的准确、详细的字符串表示。故该方法目标是给开发者使用,用于调试、重新创建对象
- Note:如果一个类没有实现 __str__()方法,那么Python在需要调用它时会用 __repr__()方法作为替代
abs方法
调用abs()方法时,Python会自动调用自定义对象的__abs__()方法
1 2 3 4 5
| print("-------------abs 方法-------------------")
my_vector8 = MyVector(3,4) num = abs(my_vector8) print("my_vector8: ", my_vector8, "abs value:", num)
|
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 方法 --------------------")
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-------------------------------------")
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")
|
需要注意的是,如果一个类没有实现__bool__()方法,那么Python在需要调用它时会调用__len__()方法来获取对象的长度信息,并根据长度是否为零来确定对象的布尔值;如果该类也没有实现 __len__()方法,则其布尔值默认为True
eq/hash方法
使用==运算符时,Python会自动调用自定义对象的__eq__()方法。如果自定义对象中没有自定义实现__eq__()方法。则会使用默认实现:使用is运算符进行判断
1 2 3 4 5 6 7 8
| print("--------------- eq 方法 --------------------")
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 )
|
调用hash()方法时,Python会自动调用自定义对象的__hash__()方法。如果自定义对象中没有自定义实现__hash__()方法。则会使用默认实现:hash( id(对象)/16 )
1 2 3 4 5 6 7 8 9
| print("--------------- hash 方法--------------------")
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) )
|
自定义对象如果需要期望可哈希,需要同时正确实现__eq__()、__hash__()两个方法。具体地:
- 如果两个对象相等,即__eq__()方法结果为True。则这两个对象的hash值结果必须相同,即__hash__()方法的结果
- 如果两个对象的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)
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}")
|
直接调用实例
实现__call__()方法,可以让类的实例成为可调用对象。此时直接调用实例,即在实例后添加圆括号,Python会自动调用自定义对象的__call__()方法
1 2 3 4 5 6
| print("---------- 直接调用实例 -----------------") bob_vector = MyVector(-12345,6789)
print(f"bob_vector 是否为可调用对象: {callable(bob_vector)}")
bob_vector()
|
参考文献
- Python编程·第3版:从入门到实践 Eric Matthes著
- Python基础教程·第3版 Magnus Lie Hetland著
- 流畅的Python·第1版 Luciano Ramalho著