【Python】笔记:接口:从协议到抽象基类

打印 上一主题 下一主题

主题 838|帖子 838|积分 2524

S11 接口:从协议到抽象基类
  1. # random.shuffle 就地打乱
  2. from random import shuffle
  3. l = list(range(10))
  4. shuffle(l)
  5. print(l)
  6. shuffle(l)
  7. print(l)
复制代码
  1. [0, 6, 3, 2, 4, 8, 5, 7, 1, 9]
  2. [0, 5, 9, 7, 6, 2, 4, 8, 1, 3]
复制代码
猴子补丁
  1. import collections
  2. Card = collections.namedtuple('Card', 'rank suit')
  3. class FrenchDeck:
  4.     ranks = [str(n) for n in range(2, 11)] + list('JQKA')
  5.     suits = 'spades diamondes clubs hearts'.split()
  6.     def __init__(self):
  7.         self._cards = [Card(rank, suit) for suit in self.suits
  8.                                         for rank in self.ranks]
  9.     def __len__(self):
  10.         return len(self._cards)
  11.     def __getitem__(self, position):
  12.         return self._cards[position]
复制代码
  1. # target: 洗牌
  2. deck = FrenchDeck()
  3. shuffle(deck)
复制代码
  1. ---------------------------------------------------------------------------
  2. TypeError                                 Traceback (most recent call last)
  3. Cell In [4], line 3
  4.       1 # target: 洗牌
  5.       2 deck = FrenchDeck()
  6. ----> 3 shuffle(deck)
  7. File c:\Users\qiany\AppData\Local\Programs\Python\Python39\lib\random.py:362, in Random.shuffle(self, x, random)
  8.     359     for i in reversed(range(1, len(x))):
  9.     360         # pick an element in x[:i+1] with which to exchange x[i]
  10.     361         j = randbelow(i + 1)
  11. --> 362         x[i], x[j] = x[j], x[i]
  12.     363 else:
  13.     364     _warn('The *random* parameter to shuffle() has been deprecated\n'
  14.     365           'since Python 3.9 and will be removed in a subsequent '
  15.     366           'version.',
  16.     367           DeprecationWarning, 2)
  17. TypeError: 'FrenchDeck' object does not support item assignment
复制代码
  1. # 打补丁
  2. def set_card(deck, position, card):
  3.     deck._cards[position] = card
  4. FrenchDeck.__setitem__ = set_card
  5. shuffle(deck)
  6. print(deck[:5])
复制代码
  1. [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')]
复制代码
定义抽象基类的子类
  1. import collections
  2. Card = collections.namedtuple('Card', 'rank suit')
  3. class FrenchDeck2(collections.MutableSequence):
  4.     ranks = [str(n) for n in range(2, 11)] + list('JQKA')
  5.     suits = 'spades diamondes clubs hearts'.split()
  6.     def __init__(self):
  7.         self._cards = [Card(rank, suit) for suit in self.suits
  8.                                         for rank in self.ranks]
  9.     def __len__(self):
  10.         return len(self._cards)
  11.     def __getitem__(self, position):
  12.         return self._cards[position]
  13.     def __setitem__(self, position, value):  # 欲实现 shuffle 须实现 __setitem__
  14.         self._cards[position] = value
  15.     def __delitem__(self, position):
  16.         del self._cards[position]
  17.     def insert(self, position, value):
  18.         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)
  1. import numbers
  2. print(1, isinstance(233, numbers.Integral))
  3. print(2, isinstance(233.33, numbers.Integral))
  4. print(3, isinstance(233.00, numbers.Integral))
  5. print(4, isinstance(True, numbers.Integral))
  6. print(5, isinstance(False, numbers.Integral))
复制代码
  1. True
  2. False
  3. False
  4. True
  5. True
复制代码
定义并使用一个抽象基类

Tombola:

  • 抽象方法:

    • load()
    • pick()

  • 具体方法:

    • loaded()
    • inspect()

  1. import abc
  2. class Tombola(abc.ABC):
  3.     @abc.abstractclassmethod  # 一般 abstractclassmethod 只有 文档字符串
  4.     def load(self, iterable):
  5.         '''可从迭代对象中添加元素'''
  6.     @abc.abstractclassmethod
  7.     def pick(self):
  8.         '''随机删除元素并返回
  9.         若果实例为空, 抛出 LookupError'''
  10.    
  11.     def loaded(self):  # 抽象基类可以包含具体实现方法
  12.         '''是否有元素'''
  13.         return bool(self.inspect())  # 抽象基类中的具体方法 只能依赖基类定义的接口(即该抽象基类中其他具体方法、抽象方法、特征)
  14.     def inspect(self):
  15.         '''返回一个由当前元素构成的有序元组'''
  16.         items = []
  17.         while True:
  18.             try:
  19.                 items.append(self.pick())
  20.             except LookupError:
  21.                 break
  22.         self.load(items)
  23.         return tuple(sorted(items))
复制代码
抽象方法可以有实现代码(不仅局限于文档字符串)
但即使实现了,子类 必须 覆盖抽象方法
或者用 super() 函数调用抽象方法
注: @abc.abstractmethod 和其他修饰器连用时, @abc.abstractmethod 应放在最内层
  1. # 不符合 Tombola 的子类
  2. class Fake(Tombola):
  3.     def pick(self):
  4.         return 13
  5. f = Fake()
复制代码
  1. ---------------------------------------------------------------------------
  2. TypeError                                 Traceback (most recent call last)
  3. Cell In [13], line 6
  4.       3     def pick(self):
  5.       4         return 13
  6. ----> 6 f = Fake()
  7. TypeError: Can't instantiate abstract class Fake with abstract method load
复制代码
  1. # Tombola 的子类 BingoCage
  2. import random
  3. class BingoCage(Tombola):
  4.     def __init__(self, items):
  5.         self._randomizer = random.SystemRandom()  # 调用 os.random() 函数, 生成"适合加密"的随机字节序列
  6.         self._items = []
  7.         self.load(items)  # 委托 load 初始化
  8.     def load(self, items):
  9.         self._items.extend(items)
  10.         self._randomizer.shuffle(self._items)  # 打乱
  11.     def pick(self):
  12.         try:
  13.             return self._items.pop()
  14.         except IndexError:
  15.             raise LookupError('pick from empty BingoCage')
  16.     def __call__(self):
  17.         self.pick()
复制代码
  1. class LotteryBlower(Tombola):
  2.     def __init__(self, iterable):
  3.         self._balls = list(iterable)
  4.     def load(self, iterable):
  5.         self._balls.extend(iterable)
  6.     def pick(self):
  7.         try:
  8.             position = random.randrange(len(self._balls))
  9.         except IndexError:
  10.             raise LookupError('pick from empty LotteryBlower')
  11.         return self._balls.pop(position)
  12.     def loaded(self):  # 重写 loaded
  13.         return bool(self._balls)
  14.    
  15.     def inspect(self):
  16.         return tuple(sorted(self._balls))
复制代码
虚拟子类

注册虚拟子类 在抽象基类上调用 register 方法, 这样, issubclass 和 isinstance 都能识别
但 注册的类 不会从抽象基类中继承如何方法或属性
  1. @Tombola.register  # 注册为 Tombola 的 虚拟子类
  2. class TomboList(list):  # 继承 list
  3.     def pick(self):
  4.         if self:  # 是否为空
  5.             position = random.randrange(len(self))
  6.             return self.pop(position)
  7.         else:
  8.             raise LookupError('pop from empty TomboList')
  9.     load = list.extend
  10.     def loaded(self):
  11.         return bool(self)
  12.    
  13.     def inspect(self):
  14.         return tuple(sorted(self))
复制代码
  1. print(1, issubclass(TomboList, Tombola))
  2. t = TomboList(range(100))
  3. print(2, isinstance(t, Tombola))
  4. print(3, TomboList.__mro__)  # 按顺序列出类及其超类
复制代码
  1. 1 True
  2. 2 True
  3. 3 (<class '__main__.TomboList'>, <class 'list'>, <class 'object'>)
复制代码
__subclasses__ 返回类的直接子类列表, 不包含虚拟子类
_abc_registry  只有抽象基类有整个属性(一个WeakSet对象)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

来自云龙湖轮廓分明的月亮

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表