在Python中,魔法方法帮助您在Python类中模拟内置函数的行为. 这些方法具有前导和尾随的双下划线(__),因此也被称为dunder methods。
这些魔法方法还帮助您在Python中实现运算符重载。您可能已经看到了一些示例。比如使用乘法运算符*与两个整数相乘得到乘积。而将其与字符串和一个整数k
相乘得到字符串重复k
次:
>>> 3 * 412>>> '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 = yv = 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)
输出 >>> 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是否相等。
输出 >>> 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)
输出 >>> True
5. __len__
Python的内置len()
函数帮助您计算内置可迭代对象的长度。比如说,对于一个向量,长度应该返回向量包含的元素数。
因此,让我们为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 2v = Vector2D(3, 5)print(len(v))
所有Vector2D
类的对象都是长度为2的:
输出 >>> 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 + v2print(result)
输出 >>> 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 - v2print(result)
输出 >>> 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): # 标量乘法 if isinstance(other, (int, float)): return Vector2D(self.x * other, self.y * other) # 内积 elif isinstance(other, Vector2D): return self.x * other.x + self.y * other.y else: raise TypeError("不支持的操作类型 *")
现在我们来看一下__mul__
方法的一些示例。
v1 = Vector2D(3, 5)v2 = Vector2D(1, 2)# 标量乘法result1 = v1 * 2print(result1) # 内积result2 = v1 * v2print(result2)
输出 >>>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("索引超出范围")
现在您可以使用索引访问元素,如下所示:
v = Vector2D(3, 5)print(v[0]) print(v[1])
输出 >>>35
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)
输出 >>> Vector2D(x=9, y=15)
11. __getattr__
使用__getattr__
方法可以获取对象的特定属性值。
对于此示例,我们可以添加一个__getattr__
方法,用于计算矢量的大小(L2范数)
:
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'对象没有属性'{name}'")
让我们验证一下这是否按预期工作:
v = Vector2D(3, 4)print(v.magnitude)
输出 >>> 5.0
结论
这就是本教程的全部内容!希望您学会了如何为类添加魔术方法以模拟内置函数的行为。
我们已经介绍了一些最有用的魔术方法。但这并不是一份详尽无遗的列表。为了进一步理解,创建一个自己选择的Python类,并根据所需的功能添加魔术方法。继续编码吧!
[Bala Priya C](https://twitter.com/balawc27)是来自印度的开发人员和技术作家。她喜欢在数学、编程、数据科学和内容创作的交叉点上工作。她的兴趣和专长领域包括DevOps、数据科学和自然语言处理。她喜欢阅读、写作、编程和咖啡!目前,她正在通过撰写教程、指南、观点文章等来学习和与开发者社区分享自己的知识。