Press "Enter" to skip to content

11个每个程序员都应该了解的Python魔术方法

11个每个程序员都应该了解的Python魔术方法 四海 第1张 

在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、数据科学和自然语言处理。她喜欢阅读、写作、编程和咖啡!目前,她正在通过撰写教程、指南、观点文章等来学习和与开发者社区分享自己的知识。

Leave a Reply

Your email address will not be published. Required fields are marked *