Python 的 Dunder 方法指南

Python 背后的魔力——Python 有几种神奇的方法——你通常会听到从业者将其称为 dunder 方法(我将它们互换地指代)。这些方法执行称为运算符重载的过程:为运算符提供超出预定义含义的扩展含义。我们使用操作重载来添加自定义…

Python 的 Dunder 方法指南

Python 背后的魔力

Python 有几种神奇的方法——你通常会听到从业者称为 dunder 方法(我将它们互换地称为它们)。这些方法执行称为运算符重载的过程:为运算符提供超出预定义含义的扩展含义。我们使用操作重载向我们的类添加自定义行为,以便我们可以将它们与 Python 的运算符和内置函数一起使用。

认为更笨的方法的最简单方法是作为实现和 Python 解释器之间的契约。合同条款之一涉及 Python 在某些给定情况下(即尝试将整数添加到自定义类)在幕后执行一些操作。

Dunder 方法以两个下划线开头和结尾:您可能遇到的最流行的是 __init__() 方法。我们在一个类中创建了 __init__() 方法,这样我们就可以使用该类的特定属性来初始化该类 — 在 Python 3 中面向对象编程入门中了解有关面向对象编程的更多信息。[0]

但是 __init__() 只是几种魔术方法中的一种。在本文中,我们将介绍您可能遇到的不同类型的 dunder 方法以及它们的作用。

String representation methods

每当我们在 Python 中创建一个新对象时,我们都会隐式创建一个相关对象,因为所有类都继承自 Object。 Object 中定义的方法由我们新创建的类继承,并在各种情况下使用,例如打印对象时。

class Car: 
pass
car = Car()print(car)"""
<__main__.Car object at 0x7f53e19a8d90>
"""

上面的代码怎么知道要打印什么?简单的。 Object 是 Python 中所有类的父类,有一个名为 __repr__() 的 dunder 方法(发音为 dunder repper);当我们调用 print() 语句时,它会从我们的 Car 对象中调用 __repr__() 方法,该方法是从父类 Object 继承的,并将一个值返回给主程序。

但是子类可以覆盖父类。我们所要做的就是在子类中创建一个具有相同名称的方法——在 Inheritance:Getting to Grips with OOP in Python 中了解更多关于继承的信息。[0]

class Car: 
def __repr__(self):
return f"{self.__class__.__qualname__}"

car = Car()
print(car)"""
Car
"""

我们还可以使用 __str__()(发音为stir)dunder 方法来创建字符串表示。 __str__() 方法返回一个人类可读的字符串,该字符串提供有关对象的更深入的信息。

注意:我们的对象没有太多内容,所以我们将使用与 __repr__() 中相同的信息。

class Car: 
def __str__(self):
return f"{self.__class__.__qualname__}"

car = Car()
print(car)"""
Car
"""

如果 __str__() 丢失,__repr__() 方法用作备份行为。因此,当您调用 print() 时,它首先查找 __str__() 以查看它是否已被定义,否则它会调用 __repr__()。

Math dunder methods

当我们创建表达式时,我们使用称为运算符的特殊符号。如果我们希望在表达式中使用操作数,那么它必须有一个数学方法,运算符可以用来计算表达式。如果不创建数学 dunder 方法,Python 会引发类型错误。

class RandomNumbers: 
def __init__(self, a, b):
self.a = a
self.b = b

set_a = RandomNumbers(2, 4)
set_b = RandomNumbers(3, 5)
print(set_a + set_b)"""
Traceback (most recent call last):
File "<string>", line 9, in <module>
TypeError: unsupported operand type(s) for +: 'RandomNumbers' and 'RandomNumbers'
"""

当然,我们可以简单地在我们的类中创建一个 add_random_numbers() 方法,但解决这个问题的更好方法是使用 __add__() dunder 方法——这样,我们可以将 + 运算符与我们的 RandomNumbers 对象一起使用。

class RandomNumbers: 
def __init__(self, a, b):
self.a = a
self.b = b

def __add__(self, other):
# Only permit RandomNumber objects to be added
if not isinstance(other, RandomNumbers):
return NotImplemented

return RandomNumbers(other.a + self.a, other.b + self.b)

def __repr__(self):
return f"{self.__class__.__qualname__}({self.a}, {self.b})"

set_a = RandomNumbers(2, 4)
set_b = RandomNumbers(3, 5)
print(set_a + set_b)"""
RandomNumbers(5, 9)
"""

当 RandomNumbers 对象位于 + 运算符的左侧时,Python 将调用 __add__() 方法:+ 运算符右侧的方法作为另一个参数传递给 __add__() 方法。

在我们的示例中,我们阻止我们的对象添加不是 RandomNumber 实例的对象。让我们看看另一种方法,它允许我们将数字与整数相乘:

class RandomNumbers: 
def __init__(self, a, b):
self.a = a
self.b = b

def __add__(self, other):
if not isinstance(other, RandomNumbers):
return NotImplemented

return RandomNumbers(other.a + self.a, other.b + self.b)

def __mul__(self, other):
if not isinstance(other, int):
return NotImplemented

return RandomNumbers(self.a * other, self.b * other)

def __repr__(self):
return f"{self.__class__.__qualname__}({self.a}, {self.b})"

set_a = RandomNumbers(2, 4)
print(set_a * 3)"""
RandomNumbers(6, 12)
"""

同样,请注意 RandomNumbers 实例位于 * 运算符的左侧。如果我们把它移到右边会发生什么?

-- snip -- set_a = RandomNumbers(2, 4)print(3 * set_a)"""
Traceback (most recent call last):
File "<string>", line 23, in <module>
TypeError: unsupported operand type(s) for *: 'int' and 'RandomNumbers'
"""

Python 引发类型错误。

这种行为的原因是当对象位于数学运算符的左侧时,会调用数学 dunder 方法。如果您想扩展功能以便还可以调用右侧的对象,那么您必须定义 reverse dunder 方法。

让我们用我们的乘法示例来证明这一点:

class RandomNumbers:
def __init__(self, a, b):
self.a = a
self.b = b

def __add__(self, other):
if not isinstance(other, RandomNumbers):
return NotImplemented

return RandomNumbers(other.a + self.a, other.b + self.b)

def __mul__(self, other):
if not isinstance(other, int):
return NotImplemented

return RandomNumbers(self.a * other, self.b * other)

def __rmul__(self, other):
return self.__mul__(other)

def __repr__(self):
return f"{self.__class__.__qualname__}({self.a}, {self.b})"

set_a = RandomNumbers(2, 4)
print(3 * set_a)"""
RandomNumbers(6, 12)
"""

Problem solved.

这些只是两种类型的 dunder 方法,但有几种——你可以在这里找到它们的列表。如果您有兴趣升级您的 Python 技能,我建议您查看 DataCamp 提供的 Python 技能和职业轨迹。[0][1]

Thanks for reading.

Connect with me:
LinkedIn
Twitter
Instagram[0][1][2]

如果您喜欢阅读这样的故事并希望支持我的写作,请考虑成为 Medium 会员。通过每月 5 美元的承诺,您可以无限制地访问 Medium 上的故事。如果您使用我的注册链接,我将收到一小笔佣金。[0][1]

已经是会员?订阅以在我发布时收到通知。[0]

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2022年5月12日
下一篇 2022年5月12日

相关推荐