首页 文章详情

Python之Magic Method魔术方法

ProjectDaedalus | 127 2024-05-18 07:36 0 0 0
UniSMS (合一短信)

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

331fd1873cc02e70f02c2e7a7ef54300.webpabstract.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))
769db2988bba8c0d3002c8dab8f12940.webpfigure 1.png

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

97bc44699684fca8c9386b13606ea8d9.webpfigure 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)
2feca6f4a701051294c6b79b0573c0cc.webpfigure 3.png

bool方法

调用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")
59ae40dcf3db5d37adca739ef7ddb424.webpfigure 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 )
a81178ab00d8bbfe36bd5b2e0f754e3a.webpfigure 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) )
ef838b9354b49ecb2cba69d9df561eaa.webpfigure 6.png

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

  1. 如果两个对象相等,即__eq__()方法结果为True。则这两个对象的hash值结果必须相同,即__hash__()方法的结果
  2. 如果两个对象的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}")
3a3da868f36edf316cbce2fd048b4cf9.webpfigure 7.png

直接调用实例

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

      
      print("---------- 直接调用实例 -----------------")
bob_vector = MyVector(-12345,6789)
# 实现__call__()方法,可以让类的实例成为可调用对象
print(f"bob_vector 是否为可调用对象: {callable(bob_vector)}")
# 直接调用实例时: Python会自动调用自定义对象的__call__()方法
bob_vector()
f48b503dbceb64d88a0a36398164ebd3.webpfigure 8.png 参考文献
  1. Python编程·第3版:从入门到实践 Eric Matthes著
  2. Python基础教程·第3版 Magnus Lie Hetland著
  3. 流畅的Python·第1版 Luciano Ramalho著
good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter