1Fluent Python(第2版)

Python数据模型

魔术方法

__repr____iter____getitem__协议

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第1章:Python数据模型

一、导读

1.1 本章学习目标

第1章讲解 Python 的数据模型——魔术方法(dunder methods)如何让自定义对象与 Python 语言的核心特性(迭代、索引、上下文管理等)无缝集成。

本章核心学习目标:

  • 理解 Python 数据模型:魔术方法如何驱动语言特性
  • 掌握核心协议:__repr__、__iter__、__getitem__、__len__
  • 理解协议(Protocol)的概念:不需要继承,只需实现特定方法
  • 学会用魔术方法让对象表现得像内置类型

1.2 为什么本章如此重要

Python 数据模型是理解 Python 语言行为的基础。掌握魔术方法,能写出"Pythonic"的代码——让自定义对象像内置类型一样自然。

1.3 核心问题引导

for x in obj 背后发生了什么?

len(obj) 和 obj.__len__() 有什么区别?

如何实现一个支持切片和迭代的自定义容器?


二、核心概念详解

2.1 魔术方法与协议

python
class Card:
    def __init__(self, suit, rank):
        self.suit = suit
        self.rank = rank
    
    def __repr__(self):
        return f'Card({self.suit}, {self.rank})'
    
    def __str__(self):
        return f'{self.rank} of {self.suit}'

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()
    
    def __init__(self):
        self._cards = [Card(suit, rank) 
                       for suit in self.suits 
                       for rank in self.ranks]
    
    def __len__(self):
        return len(self._cards)
    
    def __getitem__(self, position):
        return self._cards[position]

deck = FrenchDeck()
print(len(deck))        # 52, 调用 __len__
print(deck[0])          # Card(spades, 2), 调用 __getitem__
print(deck[-1])         # Card(hearts, A)
print(deck[12::13])     # 切片也工作

# 迭代: 没有 __iter__,Python 用 __getitem__ 实现
for card in deck:
    print(card)

# in 操作符
print(Card('hearts', 'A') in deck)  # True

# 随机选择
from random import choice
print(choice(deck))

2.2 迭代协议

python
class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = text.split()
    
    def __repr__(self):
        return f'Sentence({self.text!r})'
    
    def __iter__(self):
        return iter(self.words)
    
    def __len__(self):
        return len(self.words)

s = Sentence("Hello World Python")
for word in s:
    print(word)

# 迭代协议:
# 1. 调用 __iter__() 获取迭代器
# 2. 反复调用 __next__() 直到 StopIteration
# 3. for/in/sum/min/max 等都使用这个协议

2.3 上下文管理器协议

python
class Timer:
    def __enter__(self):
        import time
        self.start = time.perf_counter()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        import time
        self.elapsed = time.perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.4f}s")
        return False  # 不抑制异常

with Timer():
    sum(range(10_000_000))
# 输出: Elapsed: 0.1234s

三、实战案例

3.1 案例一:实现一个向量类

python
import math
from typing import Iterator

class Vector:
    def __init__(self, *components):
        self._components = tuple(components)
    
    def __repr__(self):
        return f'Vector{self._components}'
    
    def __len__(self):
        return len(self._components)
    
    def __getitem__(self, index):
        return self._components[index]
    
    def __iter__(self) -> Iterator[float]:
        return iter(self._components)
    
    def __abs__(self):
        return math.sqrt(sum(x * x for x in self))
    
    def __eq__(self, other):
        if isinstance(other, Vector):
            return self._components == other._components
        return NotImplemented
    
    def __add__(self, other):
        if isinstance(other, Vector):
            if len(self) != len(other):
                raise ValueError("Vectors must have same length")
            return Vector(*(a + b for a, b in zip(self, other)))
        return NotImplemented
    
    def __mul__(self, scalar):
        return Vector(*(x * scalar for x in self))
    
    def __rmul__(self, scalar):
        return self.__mul__(scalar)
    
    def dot(self, other):
        return sum(a * b for a, b in zip(self, other))

v1 = Vector(1, 2, 3)
v2 = Vector(4, 5, 6)
print(abs(v1))       # 3.7416...
print(v1 + v2)       # Vector(5, 7, 9)
print(v1 * 3)        # Vector(3, 6, 9)
print(3 * v1)        # Vector(3, 6, 9)
print(v1.dot(v2))    # 32
print(list(v1))      # [1, 2, 3]

四、本章小结

Python 数据模型通过魔术方法让自定义对象与语言核心特性集成

迭代协议:__iter__ + __next__ 驱动 for/in 等语法

上下文管理器:__enter__ + __exit__ 驱动 with 语句


五、思考与练习

__repr__ 和 __str__ 的区别是什么?

如何实现一个支持负索引的自定义序列?


1/8Pyth
0%