S11 介面:從協議到抽象基類
# random.shuffle 就地打亂
from random import shuffle
l = list(range(10))
shuffle(l)
print(l)
shuffle(l)
print(l)
[0, 6, 3, 2, 4, 8, 5, 7, 1, 9]
[0, 5, 9, 7, 6, 2, 4, 8, 1, 3]
猴子補丁
import collections
Card = collections.namedtuple('Card', 'rank suit')
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamondes 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]
# target: 洗牌
deck = FrenchDeck()
shuffle(deck)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [4], line 3
1 # target: 洗牌
2 deck = FrenchDeck()
----> 3 shuffle(deck)
File c:\Users\qiany\AppData\Local\Programs\Python\Python39\lib\random.py:362, in Random.shuffle(self, x, random)
359 for i in reversed(range(1, len(x))):
360 # pick an element in x[:i+1] with which to exchange x[i]
361 j = randbelow(i + 1)
--> 362 x[i], x[j] = x[j], x[i]
363 else:
364 _warn('The *random* parameter to shuffle() has been deprecated\n'
365 'since Python 3.9 and will be removed in a subsequent '
366 'version.',
367 DeprecationWarning, 2)
TypeError: 'FrenchDeck' object does not support item assignment
# 打補丁
def set_card(deck, position, card):
deck._cards[position] = card
FrenchDeck.__setitem__ = set_card
shuffle(deck)
print(deck[:5])
[Card(rank='9', suit='spades'), Card(rank='2', suit='spades'), Card(rank='5', suit='spades'), Card(rank='Q', suit='clubs'), Card(rank='10', suit='hearts')]
定義抽象基類的子類
import collections
Card = collections.namedtuple('Card', 'rank suit')
class FrenchDeck2(collections.MutableSequence):
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamondes 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]
def __setitem__(self, position, value): # 欲實作 shuffle 須實作 __setitem__
self._cards[position] = value
def __delitem__(self, position):
del self._cards[position]
def insert(self, position, value):
self._cards.insert(position, value)
繼承 MutableSequence 必須實作 __delitem__, insert
FrenchDeck2
- 從
Sequence繼承了__contains__,__iter__,__reversed__,index,count - 從
MutableSequence繼承了append,extend,pop,remove,__iadd__
標準庫中的抽象基類
collections.abs
第一層:
Iterable:Sequence,Mapping,Set,Iterator通過__iter__方法支持迭代Container:Sequence,Mapping,Set通過__contains__方法支持inSized:Sequence,Mapping,Set,MappingView通過__len__方法支持len()Callable: (None)Hashable: (None)
第二層:
Sequence:MutableSequenceMapping:MutableMappingSet:MutableSet,ItemsView,KeysViewMappingView:ItemsView,KeysView,ValuesView
numbers
NumberComplexRealRationalIntergal
eg. 檢查一個數是否為整數: isinstance(x, numbers.Integral)
isinstance(x, type)
- type 為
Intergal檢查int,bool - type 為
Real檢查int,bool,float,fractions.Fraction, Numpy中相關物件
檢查物件是否可以被 呼叫, 可用 callable()
檢查物件是否可以被 散列, 可用 isinstance(obj, Hashable)
import numbers
print(1, isinstance(233, numbers.Integral))
print(2, isinstance(233.33, numbers.Integral))
print(3, isinstance(233.00, numbers.Integral))
print(4, isinstance(True, numbers.Integral))
print(5, isinstance(False, numbers.Integral))
True
False
False
True
True
定義并使用一個抽象基類
Tombola:
-
抽象方法:
- load()
- pick()
-
具體方法:
- loaded()
- inspect()
import abc
class Tombola(abc.ABC):
@abc.abstractclassmethod # 一般 abstractclassmethod 只有 檔案字串
def load(self, iterable):
'''可從迭代物件中添加元素'''
@abc.abstractclassmethod
def pick(self):
'''隨機洗掉元素并回傳
若果實體為空, 拋出 LookupError'''
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))
抽象方法可以有實作代碼(不僅局限于檔案字串)
但即使實作了,子類 必須 覆寫抽象方法
或者用 super() 函式呼叫抽象方法
注: @abc.abstractmethod 和其他修飾器連用時, @abc.abstractmethod 應放在最內層
# 不符合 Tombola 的子類
class Fake(Tombola):
def pick(self):
return 13
f = Fake()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In [13], line 6
3 def pick(self):
4 return 13
----> 6 f = Fake()
TypeError: Can't instantiate abstract class Fake with abstract method load
# Tombola 的子類 BingoCage
import random
class BingoCage(Tombola):
def __init__(self, items):
self._randomizer = random.SystemRandom() # 呼叫 os.random() 函式, 生成"適合加密"的隨機位元組序列
self._items = []
self.load(items) # 委托 load 初始化
def load(self, items):
self._items.extend(items)
self._randomizer.shuffle(self._items) # 打亂
def pick(self):
try:
return self._items.pop()
except IndexError:
raise LookupError('pick from empty BingoCage')
def __call__(self):
self.pick()
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 IndexError:
raise LookupError('pick from empty LotteryBlower')
return self._balls.pop(position)
def loaded(self): # 重寫 loaded
return bool(self._balls)
def inspect(self):
return tuple(sorted(self._balls))
虛擬子類
注冊虛擬子類 在抽象基類上呼叫 register 方法, 這樣, issubclass 和 isinstance 都能識別
但 注冊的類 不會從抽象基類中繼承如何方法或屬性
@Tombola.register # 注冊為 Tombola 的 虛擬子類
class TomboList(list): # 繼承 list
def pick(self):
if self: # 是否為空
position = random.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))
print(1, issubclass(TomboList, Tombola))
t = TomboList(range(100))
print(2, isinstance(t, Tombola))
print(3, TomboList.__mro__) # 按順序列出類及其超類
1 True
2 True
3 (<class '__main__.TomboList'>, <class 'list'>, <class 'object'>)
__subclasses__ 回傳類的直接子類串列, 不包含虛擬子類
_abc_registry 只有抽象基類有整個屬性(一個WeakSet物件)
本文來自博客園,作者:Zinc233,轉載請注明原文鏈接:https://www.cnblogs.com/Zinc233/p/FluentPython_S11.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/539107.html
標籤:其他
下一篇:Golang反射獲取變數型別和值
