11 每个程序员都应该知道的 Python 魔法方法

liftword13小时前技术文章3



在 Python 中,魔法方法帮助你模拟 Python 类中内置函数的行为。这些方法有前后双下划线(__),因此也被称为魔法方法 。这些魔法方法也帮助你实现 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 坐标相同,则它们相等。

现在创建两个 Vector2D 对象,它们的 x 和 y 值都相等,并比较它们的相等性:

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() 函数可以帮助你计算内置可迭代对象的长度。假设对于一个向量,长度应该返回该向量包含的元素数量。

那么,让我们为 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__`方法来定义对象之间的乘法。

Let's implement let's handle

  • 标量乘法:向量与标量的乘法,以及
  • 内积:两个向量的点积
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__ 魔法方法允许您使用熟悉的方括号 [ ] 语法索引对象并访问属性或属性切片。

对于 v 类的 Vector2D 对象:

  • 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__ 魔术方法,它会被调用以计算 向量的幅度(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' object has no attribute '{name}'")

让我们验证它是否按预期工作:

v = Vector2D(3, 4)
print(v.magnitude)
Output >>> 5.0

相关文章

太好用!教你几招Python魔法方法的妙用

专注Python、AI、大数据,请关注公众号七步编程!Python是一种简单的编程语言,满足一个需求,可以有各种各样的实现方法。正是因为它可以通过各种串联满足很多复杂的逻辑,因此,对代码可读性关注度不...

Python中关于魔法方法、单例模式的知识

目录:init,del,add,str和 repr,call,单例模式,class,dict,doc,bases,mro魔法方法:定义:在特定条件下,触发方法在python里面很多以双下划线开头且结尾...

掌握Python的&quot;魔法&quot;:特殊方法与属性完全指南

在Python的世界里,以双下划线开头和结尾的"魔法成员"(如__init__、__str__)是面向对象编程的核心。它们赋予开发者定制类行为的超能力,让自定义对象像内置类型一样优雅工...

Python 魔法方法的工作原理:实用指南(三)

以前的文章我们讲了python中对象表示、运算符等魔法方法的用法,下面我们继续深入探索容器、资源管理、性能方面的魔法方法。容器方法(类似列表、字典对象)当你需要自定义存储和检索数据的对象时,此方式可以...

Python进阶——如何正确使用魔法方法?(上)

微信搜索关注「水滴与银弹」公众号,第一时间获取优质技术干货。7年资深后端研发,用简单的方式把技术讲清楚。在做 Python 开发时,我们经常会遇到以双下划线开头和结尾的方法,例如 __init__、_...