跳到主要内容

Python 特殊方法

在Python的面向对象编程中,特殊方法(也称为魔术方法或双下方法)是以双下划线开头和结尾的方法,例如 __init____str__ 等。它们赋予了类特殊的能力,让我们能够自定义对象的行为,使其与Python的内置函数和操作符无缝集成。

什么是特殊方法?

特殊方法是Python解释器调用的内置方法,允许我们定义类实例在特定情况下的行为。当我们使用特定的语法或内置函数时,Python会自动调用这些方法。

提示

特殊方法的命名遵循一个模式:两个下划线 + 方法名 + 两个下划线,例如 __init__。这种命名方式也是为什么它们被俗称为"双下方法"或"dunder方法"(double underscore)。

常用特殊方法一览

初始化与构造

  • __init__(self, ...): 初始化一个新创建的实例
  • __new__(cls, ...): 创建类的新实例(在 __init__ 之前调用)
  • __del__(self): 当对象被垃圾回收时调用

字符串表示

  • __str__(self): 定义 str() 函数的行为
  • __repr__(self): 定义 repr() 函数的行为

比较操作

  • __eq__(self, other): 定义等于操作符 == 的行为
  • __ne__(self, other): 定义不等于操作符 != 的行为
  • __lt__(self, other): 定义小于操作符 < 的行为
  • __gt__(self, other): 定义大于操作符 > 的行为
  • __le__(self, other): 定义小于等于操作符 <= 的行为
  • __ge__(self, other): 定义大于等于操作符 >= 的行为

数值运算

  • __add__(self, other): 定义加法操作符 + 的行为
  • __sub__(self, other): 定义减法操作符 - 的行为
  • __mul__(self, other): 定义乘法操作符 * 的行为
  • __truediv__(self, other): 定义除法操作符 / 的行为

容器方法

  • __len__(self): 定义 len() 函数的行为
  • __getitem__(self, key): 定义通过索引访问元素的行为,如 obj[key]
  • __setitem__(self, key, value): 定义通过索引设置元素的行为,如 obj[key] = value
  • __contains__(self, item): 定义 in 操作符的行为

详细讲解常用特殊方法

__init__ 方法

__init__ 是最常用的特殊方法之一,用于初始化新创建的对象。

python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

# 创建Person类的实例
person = Person("张三", 25)
print(f"姓名: {person.name}, 年龄: {person.age}")

输出:

姓名: 张三, 年龄: 25

__str____repr__ 方法

这两个方法都用于返回对象的字符串表示,但用途略有不同:

  • __str__ 用于返回对象的"非正式"字符串表示,主要面向用户
  • __repr__ 用于返回对象的"正式"字符串表示,主要面向开发者
python
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __str__(self):
return f"{self.name}, {self.age}岁"

def __repr__(self):
return f"Person('{self.name}', {self.age})"

person = Person("张三", 25)
print(str(person)) # 调用 __str__
print(repr(person)) # 调用 __repr__

输出:

张三, 25岁
Person('张三', 25)
备注

当我们直接在交互式解释器中输入变量名并按回车,或使用 print() 函数但没有显式调用 str()repr() 时,Python会首先尝试调用 __str__。如果没有定义 __str__,则会回退到 __repr__

比较方法

通过定义比较方法,我们可以自定义对象之间如何进行比较操作:

python
class Score:
def __init__(self, value):
self.value = value

def __eq__(self, other):
return self.value == other.value

def __gt__(self, other):
return self.value > other.value

score1 = Score(85)
score2 = Score(90)
score3 = Score(85)

print(f"score1 == score3: {score1 == score3}") # 调用 __eq__
print(f"score1 > score2: {score1 > score2}") # 调用 __gt__
print(f"score1 < score2: {score1 < score2}") # Python 自动推断

输出:

score1 == score3: True
score1 > score2: False
score1 < score2: True

算术运算方法

我们可以重载算术运算符,让自定义对象支持各种数学运算:

python
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y

def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)

def __str__(self):
return f"Vector({self.x}, {self.y})"

v1 = Vector(2, 3)
v2 = Vector(3, 4)
v3 = v1 + v2 # 调用 __add__

print(f"v1 = {v1}")
print(f"v2 = {v2}")
print(f"v1 + v2 = {v3}")

输出:

v1 = Vector(2, 3)
v2 = Vector(3, 4)
v1 + v2 = Vector(5, 7)

容器方法

通过实现以下方法,我们可以使类的实例表现得像一个容器:

python
class MyList:
def __init__(self, data):
self.data = data

def __len__(self):
return len(self.data)

def __getitem__(self, index):
return self.data[index]

def __setitem__(self, index, value):
self.data[index] = value

def __contains__(self, item):
return item in self.data

my_list = MyList([1, 2, 3, 4, 5])
print(f"长度: {len(my_list)}") # 调用 __len__
print(f"第三个元素: {my_list[2]}") # 调用 __getitem__
my_list[1] = 20 # 调用 __setitem__
print(f"修改后的列表: {[my_list[i] for i in range(len(my_list))]}")
print(f"3在列表中: {3 in my_list}") # 调用 __contains__

输出:

长度: 5
第三个元素: 3
修改后的列表: [1, 20, 3, 4, 5]
3在列表中: True

实际应用案例

案例1:创建一个自定义复数类

python
class ComplexNumber:
def __init__(self, real, imag):
self.real = real
self.imag = imag

def __add__(self, other):
return ComplexNumber(self.real + other.real, self.imag + other.imag)

def __sub__(self, other):
return ComplexNumber(self.real - other.real, self.imag - other.imag)

def __mul__(self, other):
# (a+bi)(c+di) = (ac-bd) + (ad+bc)i
real = self.real * other.real - self.imag * other.imag
imag = self.real * other.imag + self.imag * other.real
return ComplexNumber(real, imag)

def __str__(self):
if self.imag >= 0:
return f"{self.real} + {self.imag}i"
else:
return f"{self.real} - {abs(self.imag)}i"

# 创建两个复数
c1 = ComplexNumber(2, 3)
c2 = ComplexNumber(1, -1)

print(f"c1 = {c1}")
print(f"c2 = {c2}")
print(f"c1 + c2 = {c1 + c2}")
print(f"c1 - c2 = {c1 - c2}")
print(f"c1 * c2 = {c1 * c2}")

输出:

c1 = 2 + 3i
c2 = 1 - 1i
c1 + c2 = 3 + 2i
c1 - c2 = 1 + 4i
c1 * c2 = 5 + 1i

案例2:自定义日期类与比较

python
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day

def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"

def __lt__(self, other):
if self.year != other.year:
return self.year < other.year
if self.month != other.month:
return self.month < other.month
return self.day < other.day

def __eq__(self, other):
return self.year == other.year and self.month == other.month and self.day == other.day

# 创建日期对象
date1 = Date(2023, 10, 15)
date2 = Date(2023, 11, 1)
date3 = Date(2023, 10, 15)

print(f"date1: {date1}")
print(f"date2: {date2}")
print(f"date3: {date3}")
print(f"date1 < date2: {date1 < date2}")
print(f"date1 == date3: {date1 == date3}")

输出:

date1: 2023-10-15
date2: 2023-11-01
date3: 2023-10-15
date1 < date2: True
date1 == date3: True

案例3:自定义可迭代对象

python
class Fibonacci:
"""生成斐波那契数列的可迭代类"""
def __init__(self, limit):
self.limit = limit
self.a, self.b = 0, 1
self.count = 0

def __iter__(self):
# 迭代器就是对象本身
self.a, self.b = 0, 1
self.count = 0
return self

def __next__(self):
if self.count >= self.limit:
# 停止迭代
raise StopIteration

result = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return result

# 使用for循环迭代Fibonacci对象
fib = Fibonacci(10)
print("斐波那契数列的前10项:")
for num in fib:
print(num, end=" ")

输出:

斐波那契数列的前10项:
0 1 1 2 3 5 8 13 21 34

总结

Python的特殊方法是面向对象编程中非常强大的工具,它们使我们能够:

  1. 自定义对象的行为,使其像内置类型一样工作
  2. 重载运算符,使我们可以用自然的语法操作自定义对象
  3. 与Python内置函数集成,例如 len(), str(), repr()

掌握特殊方法,让我们能够写出更加"Pythonic"的代码,充分利用Python的语言特性。特殊方法的设计展现了Python的优雅哲学:通过一致的接口设计,使代码既直观又富有表现力。

练习

  1. 创建一个 BankAccount 类,实现 __add____sub__ 方法使其支持账户间的转账操作。
  2. 实现一个 Temperature 类,支持摄氏度和华氏度之间的转换,并实现特殊方法使其支持比较操作。
  3. 创建一个 Matrix 类,实现矩阵的加法、减法和乘法运算。
  4. 实现一个自定义的 Stack 类,使用特殊方法让它支持 len()、索引访问等操作。

进一步阅读