这里介绍Python中的Magic Method魔术方法
abstract.png 实践在Python中的Magic Method魔术方法是类中的特殊方法,其通常使用两个下划线进行包裹来命名(典型地:__init__()方法)。普通方法一般需要我们显式调用,而魔术方法则无需显式调用就可以自动执行。这里我们在MyVector类中实现了一些常用的魔术方法。让我们看看这些魔术方法如何自动的被调用
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__()方法
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__()方法
print("-------------abs 方法-------------------")
# 调用abs()方法时: Python会自动调用自定义对象的__abs__()方法
my_vector8 = MyVector(3,4)
num = abs(my_vector8)
print("my_vector8: ", my_vector8, "abs value:", num)
figure 3.pngbool方法
调用bool()方法 或 当对象被用于条件判断时(例如:if语句中) 时,Python会自动调用自定义对象的__bool__()方法
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运算符进行判断
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 )
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__()两个方法。具体地:
- 如果两个对象相等,即__eq__()方法结果为True。则这两个对象的hash值结果必须相同,即__hash__()方法的结果
- 如果两个对象的hash值结果相同,即__hash__()方法的结果。则这两个对象不一定会相等,即__eq__()方法结果可能为True 或 False
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__()方法
print("---------- 直接调用实例 -----------------")
bob_vector = MyVector(-12345,6789)
# 实现__call__()方法,可以让类的实例成为可调用对象
print(f"bob_vector 是否为可调用对象: {callable(bob_vector)}")
# 直接调用实例时: Python会自动调用自定义对象的__call__()方法
bob_vector()
figure 8.png
参考文献
- Python编程·第3版:从入门到实践 Eric Matthes著
- Python基础教程·第3版 Magnus Lie Hetland著
- 流畅的Python·第1版 Luciano Ramalho著