各位CSDN的朋友们好~
本期将揭晓魔术方法与Python的鸭子类型哲学的巧妙
魔术方法在Python中被称为special method
,这些方法是用户可以自定义类的方式,用于实现类的特殊行为。这些方法会在特定事件或操作发生时被Python解释器自动调用。它们的特点是名字前后都有两个下划线(如__init__
),也被称为dunder method
。
Tips:拆解单词
dunder
du=double # 双
under~=underline # 下划线
du+under=dunder # 双下划线
__add__
, __sub__
等实现运算符重载__enter__
, __exit__
实现with
语句__getitem__
, __setitem__
实现序列式操作__getattr__
, __setattr__
控制属性访问__init__
, __del__
控制对象生命周期例如用__next__
和__iter__
构造迭代器是完全正确的应用:
class Count:
def __init__(self, limit):
self.limit = limit
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n < self.limit:
result = self.n
self.n += 1
return result
else:
raise StopIteration
# 用法
for num in Count(5):
print(num) # 输出0,1,2,3,4
在CPython实现层面:
PyTypeObject
)中的函数指针__add__
对应tp_as_number->nb_add
a + b
时,解释器会:
a.__class__
的tp_as_number
结构nb_add
函数例如加法运算的底层调用链:
// CPython内部大致实现
PyNumber_Add(PyObject *v, PyObject *w)
{
PyObject *result = binary_op(v, w, NB_SLOT(nb_add));
// ...
}
__new__
, __init__
, __del__
__add__
, __sub__
, __mul__
等__len__
, __getitem__
, __setitem__
__getattr__
, __setattr__
, __delattr__
__call__
__enter__
, __exit__
__iter__
, __next__
# 另一个示例:实现向量加法
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 __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 4)
v2 = Vector(3, 5)
print(v1 + v2) # 输出: Vector(5, 9)
学过Java的朋友可以联想一下
public interface Comparable<T> {
int compareTo(T o); // 必须实现的方法
}
__lt__
表示对象可比较def __lt__(self, other): # 实现后对象就支持 < 操作
return self.value < other.value
toString()
和Python的__str__
都是字符串表示的约定维度 | Java接口(Interface) | Python魔术方法(Magic Methods) |
---|---|---|
形式 | 显式implements 声明 |
隐式实现(只需定义对应方法) |
强制性 | 必须实现接口所有方法 | 可选实现所需方法(更灵活) |
继承 | 可多继承接口 | 通过方法解析顺序(MRO)继承魔术方法 |
类型检查 | 编译时检查 | 运行时动态检查 |
使用场景 | 主要用于API设计/多继承替代 | 主要用于运算符重载/特殊行为定制 |
Java接口实现排序:
class Student implements Comparable<Student> {
private String name;
@Override
public int compareTo(Student other) {
return this.name.compareTo(other.name);
}
}
Python魔术方法实现排序:
class Student:
def __init__(self, name):
self.name = name
def __lt__(self, other): # 只需实现需要的比较方法
return self.name < other.name
# 使用时
sorted([Student('Alice'), Student('Bob')]) # 自动调用__lt__
Python魔术方法其实更接近Java的操作符重载+接口的混合体:
__add__
对应Java的+
操作符重载__iter__
/__next__
类似于Java的Iterable
接口// 显式声明实现关系
class MyList implements List, Serializable {...}
Tips: 鸭子类型:关注行为而非具体类型
“如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子”
如果它能len(),能[]
(通过__getitem__
支持索引访问),能迭代(通过__getitem__
自动支持),那它就是序列"
# 只要实现了__len__和__getitem__,Python就会将其识别为序列类型
class MySequence:
def __len__(self):
"""返回序列长度"""
return len(self.data)
def __getitem__(self, index):
"""支持索引访问和切片操作"""
if isinstance(index, slice): # 处理切片
start, stop, step = index.indices(len(self))
return [self.data[i] for i in range(start, stop, step)]
return self.data[index]
def __init__(self, data):
self.data = data
```
- 更深入地说,我们用魔术方法**实现了 Python 的类型协议**,便可以使用协议定义的功能
> **Python文档引用**:
> "A sequence is an iterable which supports efficient element access using integer indices via the **getitem**() special method and defines a **len**() method that returns the length of the sequence."
> 也就是:
> 1. **序列协议(Sequence Protocol)**:
> - `__len__()`: 返回序列长度
> - `__getitem__()`: 支持索引访问和切片操作
> - (可选)`__contains__()`: 实现`in`操作符
> - (可选)`__reversed__()`: 实现`reversed()`函数
> 2. **迭代协议(Iterable Protocol)**:
> - 实现了`__getitem__`的对象自动成为可迭代对象
> - 也可以显式实现`__iter__()`来定制迭代行为
```python
seq = MySequence([10, 20, 30, 40, 50])
print(len(seq)) # 5 ← 调用__len__
print(seq[2]) # 30 ← 调用__getitem__
print(seq[1:4]) # [20, 30, 40] ← 切片自动支持
for item in seq: # 自动成为可迭代对象
print(item) # 10, 20, 30, 40, 50
print(30 in seq) # True ← 即使没实现__contains__也能工作
场景 | Java方式 | Python方式 |
---|---|---|
定义可比较对象 | 实现Comparable 接口 |
实现__lt__ 等比较魔术方法 |
定义可迭代对象 | 实现Iterable 接口 |
实现__iter__ 和__next__ |
定义字符串表示 | 重写toString() |
实现__str__ 和__repr__ |
定义函数式对象 | 实现单一方法接口(如Runnable ) |
实现__call__ |
__new__
和__init__
这两个魔术方法有什么作用,它们的区别是什么?__str__
和__repr__
这两个魔术方法有什么区别,它们一般在什么场景下被调用?__format__
魔术方法在何时被调用,有何用途?__bin__
魔术方法的作用是什么?请看第一期:【Python】魔法方法是真的魔法! (第一期)-CSDN博客
插眼待更~