一. 什么是數據模型?
Python數據模型是Python對象系統的抽象,通過一組特殊方法?(如__init__
、__len__
等)和協議?(如迭代協議、上下文管理協議),定義了對象如何與語言的內置功能(如len()
、for
循環等)交互。
?核心思想
- ?統一性:所有對象(如列表、字典、自定義類)的行為都通過相同的特殊方法實現。
- ?靈活性:通過實現特殊方法,可以讓自定義對象支持內置操作(如
+
、in
、切片等)。
例子
import collections
Card = collections.namedtuple('Card', ['rank', '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(rank, suit) for suit in self.suitsfor rank in self.ranks]def __len__(self):return len(self._cards)def __getitem__(self, position):return self._cards[position]
解釋
模塊導入:
import collections
導入了 Python 標準庫中的?
collections
?模塊。命名元組定義:
Card
:
Card = collections.namedtuple('Card', ['rank', 'suit'])
使用?
namedtuple
?創建了一個名為?Card
?的簡單類,表示撲克牌的一張牌。每個?Card
?對象有兩個屬性:
rank
:表示牌的點數(如?'2'
,?'J'
,?'A'
?等)。suit
:表示牌的花色(如?'spades'
,?'diamonds'
?等)。類定義:
FrenchDeck
:
- 定義了一個名為?
FrenchDeck
?的類,表示一副標準的 52 張撲克牌。- 類屬性:
ranks = [str(n) for n in range(2, 11)] + list('JQKA') suits = 'spades diamonds clubs hearts'.split()
ranks
:表示牌的所有點數,包括數字牌('2'
?到?'10'
)和字母牌('J'
,?'Q'
,?'K'
,?'A'
)。suits
:表示牌的所有花色('spades'
?黑桃、'diamonds'
?方片、'clubs'
?梅花、'hearts'
?紅心)。- 構造函數?
__init__
:初始化時創建了一副完整的撲克牌,存儲在?
def __init__(self): self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]
_cards
?列表中。通過列表推導式生成所有可能的牌組合。- 方法?
__len__
:實現了特殊方法?
def __len__(self): return len(self._cards)
__len__
,使得可以通過?len(deck)
?獲取撲克牌的數量。- 方法?
__getitem__
:實現了特殊方法?
def __getitem__(self, position): return self._cards[position]
__getitem__
,使得可以通過索引訪問撲克牌,例如?deck[0]
。
常用特殊方法:
方法 | 對應操作 |
---|---|
__init__ | 對象初始化 |
__repr__ ?/?__str__ | 字符串表示 |
__len__ | len(obj) |
__getitem__ | obj[key] (支持索引和切片) |
__iter__ | for x in obj (迭代) |
__enter__ ?/?__exit__ | with 語句(上下文管理) |
迭代協議
實現__iter__
和__next__
方法,讓對象支持for
循環:
class CountDown:def __init__(self, start):self.current = startdef __iter__(self):return selfdef __next__(self):if self.current <= 0:raise StopIterationelse:self.current -= 1return self.current + 1for num in CountDown(3):print(num) # 輸出: 3 2 1
?
?解釋
類定義:
CountDown
:
定義了一個名為?
CountDown
?的類,用于實現倒計時功能。構造函數?
__init__
:
def __init__(self, start): self.current = start
初始化時接收一個參數?
start
,表示倒計時的起始值,并將其賦值給實例屬性?self.current
。方法?
__iter__
:
def __iter__(self): return self
實現了可迭代協議,使得該類的實例可以作為迭代器使用。返回自身(
self
)。方法?
__next__
:
def __next__(self): if self.current <= 0: raise StopIteration else: self.current -= 1 return self.current + 1
實現了迭代器協議中的?
__next__
?方法:
- 如果當前值?
self.current
?小于或等于 0,則拋出?StopIteration
?異常,表示迭代結束。- 否則,將?
self.current
?減 1,并返回減 1 前的值(即當前值)。使用場景:
- 該類可以通過?
for
?循環或手動調用?next()
?方法進行倒計時。- 示例:
輸出結果:countdown = CountDown(5) for value in countdown:print(value)
5 4 3 2 1
上下文管理協議
實現__enter__
和__exit__
方法,支持with
語句:
class Timer:def __enter__(self):self.start = time.time()return selfdef __exit__(self, exc_type, exc_val, exc_tb):self.end = time.time()print(f"耗時: {self.end - self.start:.2f}秒")with Timer():time.sleep(1) # 輸出: 耗時: 1.00秒
?解釋
類定義:
Timer
:
定義了一個名為?
Timer
?的類,用于測量代碼塊的執行時間。方法?
__enter__
:
def __enter__(self): self.start = time.time() return self
- 實現了上下文管理協議中的?
__enter__
?方法。- 在進入?
with
?語句塊時,記錄當前時間(以秒為單位)并存儲在實例屬性?self.start
?中。- 返回自身(
self
),以便在?with
?語句塊中可以訪問該對象。方法?
__exit__
:
def __exit__(self, exc_type, exc_val, exc_tb): self.end = time.time() print(f"耗時: {self.end - self.start:.2f}秒")
- 實現了上下文管理協議中的?
__exit__
?方法。- 在退出?
with
?語句塊時,記錄當前時間(以秒為單位)并存儲在實例屬性?self.end
?中。- 計算執行時間(
self.end - self.start
),并以兩位小數的格式打印到控制臺。使用場景:
- 通過?
with
?語句使用?Timer
?類來測量代碼塊的執行時間。- 示例:
with Timer(): time.sleep(1) # 輸出: 耗時: 1.00秒
- 在?
with
?語句塊中調用了?time.sleep(1)
,模擬了一個耗時 1 秒的操作。Timer
?類會自動計算并打印出這段代碼的執行時間。?
讓自定義類支持切片
class MySequence:def __init__(self, data):self.data = datadef __len__(self):return len(self.data)def __getitem__(self, index):if isinstance(index, slice):return self.data[index.start : index.stop : index.step]else:return self.data[index]seq = MySequence([0, 1, 2, 3, 4])
print(seq[1:3]) # [1, 2](支持切片!)
鴨子類型(Duck Typing)?
Python不檢查對象的類型,而是檢查對象是否實現了特定的方法(即是否“像鴨子一樣叫”)。
?示例:
class FakeList:def __len__(self):return 10def __getitem__(self, index):return index * 2fake = FakeList()
print(len(fake)) # 10(調用__len__)
print(fake[5]) # 10(調用__getitem__)
print(isinstance(fake, list)) # False,但行為類似列表!
?
?
?
?
?