python 序列协议
| ,
| ,
| , , , , and
|
>>> class Foo:
def __getitem__(self, pos):
return range(0, 10)[pos]
>>> f = Foo()
>>> f[1]
1
>>> list(f)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> 2 in f
True
>>> 20 in f
False
Foo 类没有继承 abc.Sequence, 而且只实现了序列协议的一个方法: __getitem__ (没有实现 __len__ 方 法),足够访问元素、 迭代(__iter__)和使用 in(__contains__) 运算符了。
|
| , , , ,
| Inherited methods and , , , , , and
|
>>> class Foo:
def __init__(self):
self.data = [1, 2, 3, 4, 5]
def __getitem__(self, pos):
return self.data[pos]
def __setitem__(self, pos, value):
self.data[pos] = value
def __len__(self):
return len(self.data)
>>> f = Foo()
>>> random.shuffle(f)
>>> list(f)
[2, 4, 1, 3, 5]
Foo 类没有继承 abc.MutableSequence,通过实现可变序列协议(__len__、__setitem__)。
实现特定的协议(对象类型无关紧要)也称为“鸭子类型”。
|
|
>>> class Struggle:
def __len__(self):
return 10
>>> from collections import abc
>>> isinstance(Struggle(), abc.Sized)
True
无需显式继承、注册,abc.Sized 也能把 Struggle 识别为自己的子类, 只要实现了特殊方法 __len__ 即可。
继承
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck(collections.MutableSequence):
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]
def __setitem__(self, position, value):
self._cards[position] = value
def __delitem__(self, position):
del self._cards[position]
def insert(self, position, value):
self._cards.insert(position, value)
继承必须实现抽象方法。
抽象基类
自定义抽象基类
import abc
class Tombola(abc.ABC):
"""抽象基类要继承 abc.ABC"""
@abc.abstractmethod
def load(self, iterable):
"""从可迭代对象中添加元素"""
@abc.abstractmethod
def pick(self):
"""随机删除元素,然后将其返回。
如果实例为空,抛出'LookupError'"""
def loaded(self):
"""如果至少有一个元素, 返回`True`, 否则返回`False`。 """
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))
import random
from tombola import Tombola
class BingoCage(Tombola):
def __init__(self, items):
self._randomizer = random.SystemRandom()
self._items = []
self.load(items)
def load(self, items):
self._items.extend(items)
self._randomizer.shuffle(self._items)
def pick(self):
try:
return self._items.pop()
except IndexError:
# IndexError 是 LookupError 的子类
raise LookupError('pick from empty BingoCage')
def __call__(self):
self.pick()
import random
from tombola import 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
from tombola import Tombola
@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))
# Tombola.register(TomboList) # 方法注册虚拟子类
>>> from tombola import Tombola
>>> from tombolist import TomboList
>>> issubclass(TomboList, Tombola)
True
>>> t = TomboList(range(100))
>>> isinstance(t, Tombola)
True
注意:
>>> TomboList.__mro__
(<class 'tombolist.TomboList'>, <class 'list'>, <class 'object'>)
TomboList 类的 __mro__ 属性, 只有“真实的”超类,没有 Tombola, 因此 Tombolist 不会从 Tombola 中继承任何方法。
register的方式:
Sequence.register(tuple)
Sequence.register(str)
Sequence.register(range)
Sequence.register(memoryview)