譯者 | 布加迪
審校 | 重樓
在Python/ target=_blank class=infotextkey>Python中,魔術方法(Magic Method)可以幫助您模擬Python類中內置函數的行為。這些方法有前后雙下劃線(__),因此也被稱為Dunder方法。
這些魔術方法還可以幫助您在Python中實現操作符重載。您可能見過這樣的例子,就像兩個整數與乘法運算符*一起使用得到乘積一樣。當它與字符串和整數k一起使用時,字符串會重復k次:
>>> 3 * 4
12
>>> 'code' * 3
'codecodecode'
我們在本文中將通過創建一個簡單的二維向量Vector2D類來探索Python中的魔術方法。
我們將從您可能熟悉的方法入手,逐步構建更有幫助的魔術方法。
不妨開始編寫一些魔術方法!
1. __init__
考慮下面的Vector2D類:
class Vector2D:
pass
一旦您創建了類,并實例化對象,就可以添加如下屬性:obj_name.attribute_name = value。
然而,您需要在實例化對象時初始化這些屬性,而不是手動向創建的每個實例添加屬性(當然,這一點也不有趣!)。
為此,您可以定義__init__方法。不妨為Vector2D類定義__init__方法:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
v = Vector2D(3, 5)
2. __repr__
當您嘗試檢查或打印輸出實例化的對象時,您將發現沒有得到任何有幫助的信息。
v = Vector2D(3, 5)
print(v)
Output >>> <__mAIn__.Vector2D object at 0x7d2fcfaf0ac0>
這就是為什么您應該添加一個表示字符串,一個對象的字符串表示。為此,添加__repr__方法,如下所示:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
v = Vector2D(3, 5)
print(v)
Output >>> Vector2D(x=3, y=5)
__repr__應該包含創建類實例所需的所有屬性和信息。__repr__方法通常用于調試目的。
3. __str__
__str__也用于添加對象的字符串表示。通常,__str__方法用于為類的最終用戶提供信息。
不妨給我們的類添加一個__str__方法:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Vector2D(x={self.x}, y={self.y})"
v = Vector2D(3, 5)
print(v)
Output >>> Vector2D(x=3, y=5)
如果沒有__str__的實現,它就返回到__repr__。因此對于您創建的每個類,您至少應該添加__repr__方法。
4. __eq__
接下來,不妨添加一個方法來檢查Vector2D類的任意兩個對象是否相等。如果兩個向量有相同的x和y坐標,它們是相等的。
現在創建兩個具有相等x和y值的Vector2D對象,并比較它們是否相等:
v1 = Vector2D(3, 5)
v2 = Vector2D(3, 5)
print(v1 == v2)
結果為False,因為默認情況下比較會檢查內存中對象ID是否相等。
Output >>> False
不妨添加__eq__方法來檢查是否相等:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
檢查相等性現在應該按預期工作:
v1 = Vector2D(3, 5)
v2 = Vector2D(3, 5)
print(v1 == v2)
Output >>> True
5. __len__
Python的內置len()函數可以幫助您計算內置可迭代對象(iterable)的長度。比如說,就向量而言,length應該返回該向量所包含的元素的個數。
所以不妨為Vector2D類添加一個__len__方法:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __len__(self):
return 2
v = Vector2D(3, 5)
print(len(v))
Vector2D類的所有對象長度為2:
Output >>> 2
6. __add__
現在不妨考慮對向量執行的常見運算。不妨添加魔術方法來加減任意兩個向量。
如果您直接嘗試添加兩個向量對象,就會遇到錯誤。所以您應該添加一個__add__方法:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __add__(self, other):
return Vector2D(self.x + other.x, self.y + other.y)
您現在可以像這樣添加任意兩個向量:
v1 = Vector2D(3, 5)
v2 = Vector2D(1, 2)
result = v1 + v2
print(result)
Output >>> Vector2D(x=4, y=7)
7. __sub__
接下來,不妨添加一個__sub__方法來計算Vector2D類的任意兩個對象之間的差異:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __sub__(self, other):
return Vector2D(self.x - other.x, self.y - other.y)
v1 = Vector2D(3, 5)
v2 = Vector2D(1, 2)
result = v1 - v2
print(result)
Output >>> Vector2D(x=2, y=3)
8. __mul__
我們還可以定義__mul__方法來定義對象之間的乘法。
不妨來處理:
- 標量乘法:向量與標量的乘法
- 內積:兩個向量的點積
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __mul__(self, other):
# Scalar multiplication
if isinstance(other, (int, float)):
return Vector2D(self.x * other, self.y * other)
# Dot product
elif isinstance(other, Vector2D):
return self.x * other.x + self.y * other.y
else:
raise TypeError("Unsupported operand type for *")
現在我們將舉幾個例子,看看__mul__方法是如何實際工作的。
v1 = Vector2D(3, 5)
v2 = Vector2D(1, 2)
# Scalar multiplication
result1 = v1 * 2
print(result1)
# Dot product
result2 = v1 * v2
print(result2)
Output >>>
Vector2D(x=6, y=10)
13
9. __getitem__
__getitem__魔術方法讓您可以索引對象,并使用熟悉的方括號[]語法訪問屬性或屬性切片。
對于Vector2D類的對象v:
- v [0]:x坐標
- v [1]:y坐標
如果您嘗試通過索引訪問,您會遇到錯誤:
v = Vector2D(3, 5)
print(v[0],v[1])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in ()
----> 1 print(v[0],v[1])
TypeError: 'Vector2D' object is not subscriptable
不妨實現__getitem__ 方法:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __getitem__(self, key):
if key == 0:
return self.x
elif key == 1:
return self.y
else:
raise IndexError("Index out of range")
現在您可以使用索引訪問這些元素,如下所示:
v = Vector2D(3, 5)
print(v[0])
print(v[1])
Output >>>
3
5
10. __call__
借助__call__方法的實現,您可以像調用函數一樣調用對象。
在Vector2D類中,我們可以實現__call__,按給定因子縮放向量:
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __call__(self, scalar):
return Vector2D(self.x * scalar, self.y * scalar)
如果您現在調用3,會得到縮放3倍的向量:
v = Vector2D(3, 5)
result = v(3)
print(result)
Output >>> Vector2D(x=9, y=15)
11. __getattr__
__getattr__方法用于獲取對象的特定屬性的值。
就這個例子而言,我們可以添加一個__getattr__ dunder方法,一旦被調用可計算向量的量值(L2-norm):
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector2D(x={self.x}, y={self.y})"
def __getattr__(self, name):
if name == "magnitude":
return (self.x ** 2 + self.y ** 2) ** 0.5
else:
raise AttributeError(f"'Vector2D' object has no attribute '{name}'")
不妨驗證這是否像預期的那樣工作:
v = Vector2D(3, 4)
print(v.magnitude)
Output >>> 5.0
結論
這就是本教程的全部內容!希望您已經學會了如何為您的類添加魔術方法,以模擬內置函數的行為。
我們已介紹了一些最有用的魔術方法,但這并非詳盡的清單。為了進一步理解,您可以創建一個所選擇的Python類,根據所需的功能添加魔術方法。最后祝編程愉快!
原文標題:Harness the Power of AI for Business,作者:Bala Priya C