当前位置:
首页 > temp > python入门教程 >
-
【Python】笔记:接口:从协议到抽象基类
# 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))
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对象)
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数