從協議到抽象基類
阿新 • • 發佈:2021-02-11
序列協議
class Foo:
def __getitem__(self, pos):
return range(0, 30, 10)[pos]
f = Foo()
# 訪問元素
f[1]
# Foo例項可迭代
for i in f: print(i)
# 也能支援in運算
20 in f
15 in f
如果沒有__iter__和__contains__方法,Python會呼叫__getitem__方法,設法讓迭代和in運算子可用。
動態協議和猴子補丁
- FrenchDeck物件不支援賦值操作,因此內建的random.shuffle函式不能打亂例項
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.suits for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
- 為FrenchDeck打猴子補丁,把它變成可變的,讓random.shuffle能夠處理
猴子補丁:在執行時修改類或模組,而不改動原始碼。
def set_card(deck, position, card):
deck._cards[position] = card
FrenchDeck. __setitem__ = set_card
deck = FrenchDeck()
shuffle(deck)
deck[:5]
抽象基類
import abc
class Tombola(abc.ABC):
@abc.abstractmethod
def load(self, iterable):
""""""
@abc.abstractmethod
def pick(self):
""""""
def loaded(self):
return bool(self.inspect())
def inspect(self):
items = []
while True:
try:
items.append(self.pick())
except LookupError:
break
self.load(items)
return tuple(sorted(items))
- 抽象方法中必須有文件字串,否則會報錯
- 實現Tombola具體子類
class LotteryBlower(Tombola):
def __init__(self, iterable):
self._balls = list(iterable)
def load(self, iterable):
self._balls.extend(iterable)
def pick(self):
try:
position = random.randrange(len(self._balls))
except ValueError:
raise LookupError('pick from empty LotteryBlower')
return self._balls.pop(position)
def loaded(self):
return bool(self._balls)
def inspect(self):
return tuple(sorted(self._balls))
- 虛擬子類
註冊的類不會從抽象基類中繼承任何方法或屬性
from random import randrange
@Tombola.register
class TomboList(list):
def pick(self):
if self:
position = randrange(len(self))
return self.pop(position)
else:
raise LookupError('pop from empty TomboList')
load = list.extend
def loaded(self):
return bool(self)
def inspect(self):
return tuple(sorted(self))
issubclass(TomboList, Tombola) # 是Tombola子類
t = TomboList(range(100))
isinstance(t, Tombola) # TomboList例項是Tombola子類例項
# TomboList的超類
TomboList.__mro__
# 沒有Tombola,因此沒有從中繼承任何方法
總結
- 再次提到了序列協議的__getitem__方法
- Python是動態語言,可以在程式執行是用猴子補丁修改類或模組
- 定義抽象類和抽象方法
- 現實抽象子類和虛擬子類
流暢的Python