介绍
在本教程中,我们将介绍与数值操作相关的 Python 魔术方法(magic methods)。魔术方法是 Python 类中以双下划线(__
)开头和结尾的特殊方法,也被称为 "dunder" 方法(双下划线)。
这些魔术方法允许你定义类的实例在特定操作(如加法或减法)中的行为。
我们将涵盖以下部分:
- 一元运算符
- 二元运算符
- 就地操作
让我们开始吧!
在本教程中,我们将介绍与数值操作相关的 Python 魔术方法(magic methods)。魔术方法是 Python 类中以双下划线(__
)开头和结尾的特殊方法,也被称为 "dunder" 方法(双下划线)。
这些魔术方法允许你定义类的实例在特定操作(如加法或减法)中的行为。
我们将涵盖以下部分:
让我们开始吧!
一元操作是涉及单个操作数的操作,例如取反、绝对值等。
让我们从一个简单的对象开始。在 number.py
中,创建一个名为 MyNumber
的类,该类具有一个属性 value
。
class MyNumber:
def __init__(self, value: float):
self.value = value
__neg__
魔术方法定义了取反操作的行为。当你对类的实例使用 -
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __neg__(self) -> 'MyNumber':
"""返回实例值的取反结果。"""
return MyNumber(-self.value)
__abs__
魔术方法定义了绝对值操作的行为。当你对类的实例使用 abs()
函数时,会调用此方法。
## ... (number.py 中的先前代码)
def __abs__(self) -> 'MyNumber':
"""返回实例值的绝对值。"""
return MyNumber(abs(self.value))
__round__
魔术方法定义了四舍五入操作的行为。当你对类的实例使用 round()
函数时,会调用此方法。
## ... (number.py 中的先前代码)
def __round__(self, ndigits: int = None) -> 'MyNumber':
"""将实例值四舍五入到最接近的整数或指定的小数位数。"""
return MyNumber(round(self.value, ndigits))
__floor__
魔术方法定义了向下取整操作的行为。当你对类的实例使用 math.floor()
函数时,会调用此方法。
## 需要在 number.py 的顶部导入 math 模块
import math
## ... (number.py 中的先前代码)
def __floor__(self) -> 'MyNumber':
"""返回小于或等于实例值的最大整数。"""
return MyNumber(math.floor(self.value))
__ceil__
魔术方法定义了向上取整操作的行为。当你对类的实例使用 math.ceil()
函数时,会调用此方法。
## ... (number.py 中的先前代码)
def __ceil__(self) -> 'MyNumber':
"""返回大于或等于实例值的最小整数。"""
return MyNumber(math.ceil(self.value))
现在我们已经为 MyNumber
类定义了一元运算符,让我们在 unary_example.py
中看看它们是如何工作的:
import math
from number import MyNumber
## 创建一个新的 MyNumber 对象
a = MyNumber(5)
## 使用 __neg__ 方法并打印结果
print(f'{a.value=}, {-a.value=}') ## 输出: a.value=5, -a.value=-5
## 创建另一个新的 MyNumber 对象
a = MyNumber(-5)
## 使用 __abs__ 方法并打印结果
print(f'{a.value=}, {abs(a).value=}') ## 输出: a.value=-5, abs(a).value=5
## 创建第三个新的 MyNumber 对象
a = MyNumber(5.678)
## 使用 __round__ 方法并打印结果
print(f'{a.value=}, {round(a, 2).value=}') ## 输出: a.value=5.678, round(a, 2).value=5.68
## 使用 __floor__ 方法并打印结果
print(f'{a.value=}, {math.floor(a).value=}') ## 输出: a.value=5.678, math.floor(a).value=5
## 使用 __ceil__ 方法并打印结果
print(f'{a.value=}, {math.ceil(a).value=}') ## 输出: a.value=5.678, math.ceil(a).value=6
然后在终端中输入以下命令来执行脚本。
python unary_example.py
二元操作是涉及两个操作数的操作,例如加法、减法、乘法和除法等算术运算,以及等于、不等于、小于、大于等比较运算。
__add__
魔术方法定义了加法操作的行为。当你对类的实例使用 +
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __add__(self, other: 'MyNumber') -> 'MyNumber':
"""返回实例值与其他实例值的和。"""
return MyNumber(self.value + other.value)
__sub__
魔术方法定义了减法操作的行为。当你对类的实例使用 -
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __sub__(self, other: 'MyNumber') -> 'MyNumber':
"""返回实例值与其他实例值的差。"""
return MyNumber(self.value - other.value)
__mul__
魔术方法定义了乘法操作的行为。当你对类的实例使用 *
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __mul__(self, other: 'MyNumber') -> 'MyNumber':
"""返回实例值与其他实例值的乘积。"""
return MyNumber(self.value * other.value)
__truediv__
魔术方法定义了真除法操作的行为。当你对类的实例使用 /
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __truediv__(self, other: 'MyNumber') -> 'MyNumber':
"""返回实例值除以其他实例值的结果。"""
return MyNumber(self.value / other.value)
__floordiv__
魔术方法定义了地板除法操作的行为。当你对类的实例使用 //
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __floordiv__(self, other: 'MyNumber') -> 'MyNumber':
"""返回实例值除以其他实例值的结果的最大整数部分。"""
return MyNumber(self.value // other.value)
__mod__
魔术方法定义了取模操作的行为。当你对类的实例使用 %
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __mod__(self, other: 'MyNumber') -> 'MyNumber':
"""返回实例值除以其他实例值的余数。"""
return MyNumber(self.value % other.value)
__pow__
魔术方法定义了幂运算操作的行为。当你对类的实例使用 **
运算符或 pow()
函数时,会调用此方法。
## ... (number.py 中的先前代码)
def __pow__(self, other: 'MyNumber') -> 'MyNumber':
"""返回实例值的其他实例值次幂。"""
return MyNumber(self.value ** other.value)
现在我们已经为 MyNumber
类定义了二元运算符,让我们在 binary_example.py
中看看它们是如何工作的:
from number import MyNumber
## 创建两个新的 MyNumber 对象
a = MyNumber(5)
b = MyNumber(3)
print(f'{a.value=}, {b.value=}') ## 输出: a.value=5, b.value=3
## 使用 __add__ 方法并打印结果
print(f'{(a+b).value=}') ## 输出: (a+b).value=8
## 使用 __sub__ 方法并打印结果
print(f'{(a-b).value=}') ## 输出: (a-b).value=2
## 使用 __mul__ 方法并打印结果
print(f'{(a*b).value=}') ## 输出: (a*b).value=15
## 使用 __truediv__ 方法并打印结果
print(f'{(a/b).value=}') ## 输出: (a/b).value=1.6666666666666667
## 使用 __floordiv__ 方法并打印结果
print(f'{(a//b).value=}') ## 输出: (a//b).value=1
## 使用 __mod__ 方法并打印结果
print(f'{(a%b).value=}') ## 输出: (a%b).value=2
## 使用 __pow__ 方法并打印结果
print(f'{(a**b).value=}') ## 输出: (a**b).value=125
然后在终端中输入以下命令来执行脚本。
python binary_example.py
就地操作是直接修改对象值而不创建新对象的操作,通常通过增强赋值运算符表示,例如 +=
、-=
、*=
、/=
等。
如果 Python 类未定义就地操作符,则在尝试执行就地操作时,将使用二元操作符代替。
在 inplace_example1.py
中有一个示例,将二元操作符改为就地操作符:
from number import MyNumber
## 创建两个新的 MyNumber 对象
a = MyNumber(5)
b = MyNumber(3)
print(f'{a.value=}, {b.value=}') ## 输出: a.value=5, b.value=3
a += b
## 使用 __add__ 方法并打印结果
print(f'after a+=b: {a.value=}') ## 输出:after a+=b: (a+b).value=8
要运行此示例,请在终端中输入以下命令:
python inplace_example1.py
结果显示,在尝试执行 +=
操作时,使用了 __add__
方法。
接下来,我们将在 MyNumber
中实现就地操作,它们的行为与对应的二元操作符略有不同。
__iadd__
魔术方法定义了就地加法操作的行为。当你对类的实例使用 +=
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __iadd__(self, other: 'MyNumber') -> 'MyNumber':
"""将其他实例的值加到当前实例的值上(就地操作)。"""
print(f'input: {self.value=}, {other.value=}')
self.value += other.value
print(f'after +=: {self.value=}')
return self
__isub__
魔术方法定义了就地减法操作的行为。当你对类的实例使用 -=
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __isub__(self, other: 'MyNumber') -> 'MyNumber':
"""从当前实例的值中减去其他实例的值(就地操作)。"""
print(f'input: {self.value=}, {other.value=}')
self.value -= other.value
print(f'after -=: {self.value=}')
return self
__imul__
魔术方法定义了就地乘法操作的行为。当你对类的实例使用 *=
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __imul__(self, other: 'MyNumber') -> 'MyNumber':
"""将当前实例的值乘以其他实例的值(就地操作)。"""
print(f'input: {self.value=}, {other.value=}')
self.value *= other.value
print(f'after *=: {self.value=}')
return self
__itruediv__
魔术方法定义了就地真除法操作的行为。当你对类的实例使用 /=
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __itruediv__(self, other: 'MyNumber') -> 'MyNumber':
"""将当前实例的值除以其他实例的值(就地操作)。"""
print(f'input: {self.value=}, {other.value=}')
self.value /= other.value
print(f'after /=: {self.value=}')
return self
__ifloordiv__
魔术方法定义了就地地板除法操作的行为。当你对类的实例使用 //=
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __ifloordiv__(self, other: 'MyNumber') -> 'MyNumber':
"""将当前实例的值除以其他实例的值并取整(就地操作)。"""
print(f'input: {self.value=}, {other.value=}')
self.value //= other.value
print(f'after //=: {self.value=}')
return self
__imod__
魔术方法定义了就地取模操作的行为。当你对类的实例使用 %=
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __imod__(self, other: 'MyNumber') -> 'MyNumber':
"""将当前实例的值对其他实例的值取模(就地操作)。"""
print(f'input: {self.value=}, {other.value=}')
self.value %= other.value
print(f'after %=: {self.value=}')
return self
__ipow__
魔术方法定义了就地幂运算操作的行为。当你对类的实例使用 **=
运算符时,会调用此方法。
## ... (number.py 中的先前代码)
def __ipow__(self, other: 'MyNumber') -> 'MyNumber':
"""将当前实例的值提升为其他实例值的幂(就地操作)。"""
print(f'input: {self.value=}, {other.value=}')
self.value **= other.value
print(f'after **=: {self.value=}')
return self
现在我们已经为 MyNumber
类定义了就地操作符,让我们在 inplace_example2.py
中看看它们是如何工作的:
from number import MyNumber
## 创建一个新的 MyNumber 对象
a = MyNumber(13)
## 使用 __iadd__ 方法
a += MyNumber(5)
## 输出:
## input: self.value=13, other.value=5
## after +=: self.value=18
## 使用 __isub__ 方法
a -= MyNumber(5)
## 输出:
## input: self.value=18, other.value=5
## after -=: self.value=13
## 使用 __imul__ 方法
a *= MyNumber(5)
## 输出:
## input: self.value=13, other.value=5
## after *=: self.value=65
## 使用 __itruediv__ 方法
a /= MyNumber(5)
## 输出:
## input: self.value=65, other.value=5
## after /=: self.value=13.0
## 使用 __ifloordiv__ 方法
a //= MyNumber(2)
## 输出:
## input: self.value=13.0, other.value=2
## after //=: self.value=6.0
## 使用 __imod__ 方法
a %= MyNumber(4)
## 输出:
## input: self.value=6.0, other.value=4
## after %=: self.value=2.0
## 使用 __ipow__ 方法
a **= MyNumber(3)
## 输出:
## input: self.value=2.0, other.value=3
## after **=: self.value=8.0
然后在终端中输入以下命令来执行脚本。
python inplace_example2.py
在本教程中,我们探讨了与数值操作相关的 Python 魔术方法(magic methods),这些方法允许你为类定义与不同类型数值操作交互时的自定义行为。我们涵盖了一元运算符、二元运算符和就地操作,并学习了如何实现每个魔术方法。
通过在自定义类中实现这些魔术方法,你可以创建直观且易于使用的对象,这些对象能够与标准的 Python 操作无缝协作。这不仅提高了代码的可读性,还使其更易于维护和用户友好。
随着你继续提升 Python 技能,可以尝试使用其他魔术方法,进一步自定义类的行为,并创建更强大的抽象。