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__ 方法支持 in
- Sized : Sequence, Mapping, Set, MappingView 通过 __len__ 方法支持 len()
- Callable : (None)
- Hashable : (None)
第二层:
- Sequence : MutableSequence
- Mapping : MutableMapping
- Set : MutableSet, ItemsView, KeysView
- MappingView : ItemsView, KeysView, ValuesView
numbers
- Number
- Complex
- Real
- Rational
- Intergal
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))
复制代码 定义并使用一个抽象基类
Tombola:
- 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对象)
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |