python 中的序列协议一览
在Python中,序列(Sequence)是一种基本的数据结构,用于存储一系列有序的元素。序列是Python中最常见的数据类型之一,它包括字符串(String)、列表(List)、元组(Tuple)等。
序列的特点是可以通过索引访问其中的元素,索引从0开始,并且支持切片操作,可以获取序列的子序列。此外,序列还可以进行迭代、拼接、重复、比较等操作。
在 python 语言中,通过存储数据的类型 和是否可变 可以分为以下 4 种序列:
容器序列
扁平序列
str
bytes
bytearray
arrar.array
可变序列
list
deque
bytearray
array
不可变序列
那么关于序列这种对象,其序列协议又是如何的,是如何实现的? 带着这个问题,我们来学习一下 python 序列对象
collections 中 Sequence/MutableSequence 首先,我们来看一下,在 python 中有这样一个库:collections, 其中包含了一些容器类的实现,其中序列包含 Sequence && MutableSequence, 其基类的实现关系又是如何的?
我们可以通过查看源码的方式来看一下不可变序列的协议:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Sequence (Collection[_T_co], Reversible[_T_co], Generic [_T_co] ): @overload @abstractmethod def __getitem__ (self, index: int ) -> _T_co: ... @overload @abstractmethod def __getitem__ (self, index: slice ) -> Sequence [_T_co]: ... def index (self, value: Any , start: int = ..., stop: int = ... ) -> int : ... def count (self, value: Any ) -> int : ... def __contains__ (self, value: object ) -> bool : ... def __iter__ (self ) -> Iterator[_T_co]: ... def __reversed__ (self ) -> Iterator[_T_co]: ... class MutableSequence (Sequence [_T], Generic [_T] ): @abstractmethod def insert (self, index: int , value: _T ) -> None : ... @overload @abstractmethod def __getitem__ (self, index: int ) -> _T: ... @overload @abstractmethod def __getitem__ (self, index: slice ) -> MutableSequence[_T]: ... @overload @abstractmethod def __setitem__ (self, index: int , value: _T ) -> None : ... @overload @abstractmethod def __setitem__ (self, index: slice , value: Iterable[_T] ) -> None : ... @overload @abstractmethod def __delitem__ (self, index: int ) -> None : ... @overload @abstractmethod def __delitem__ (self, index: slice ) -> None : ... def append (self, value: _T ) -> None : ... def clear (self ) -> None : ... def extend (self, values: Iterable[_T] ) -> None : ... def reverse (self ) -> None : ... def pop (self, index: int = ... ) -> _T: ... def remove (self, value: _T ) -> None : ... def __iadd__ (self: TypeshedSelf, values: Iterable[_T] ) -> TypeshedSelf: ...
对于一个不可变序列来说,首先是属于一个容器对象 ,对于容器对象而言,in/not in 是经常判断一个数据是否在这个容器中,例如:
1 2 3 4 5 6 contains = [1 , 2 , 3 ] if 1 in contains: print ("yes" ) else : print ("no" )
实现这个协议的魔法方法是:__contains__
1 def __contains__ (self, value: object ) -> bool : ...
sequence 对象是一个不可变对象,那么是如何实现的?
答案是 __getitem__
1 2 3 4 5 6 @overload @abstractmethod def __getitem__ (self, index: int ) -> _T_co: ...@overload @abstractmethod def __getitem__ (self, index: slice ) -> Sequence [_T_co]: ...
我们可以看到 sequence 对象中只实现了 __getitem__
方法,只可获取对象,于是内部数据就是不可变了,不可修改;
而实现了__setitem__
&&__delitem__
表示可以对数据进行修改/删除。
对于一个容器序列,如何获取其中的元素,例如 for … in .. 操作。这是因为实现了 __iter__
魔法方法
1 def __iter__ (self ) -> Iterator[_T_co]: ...
序列的合并
当对两个序列进行合并时,可以使用 +=,例如:
这个是如何实现的,其实是因为魔法方法 __iadd__
的实现
通过对两个序列类的学习,我们了解了序列类的一些协议,这样我们就可以基于这些知识自己实现一个序列。
可切片的序列类 我们先通过 list 来了解一下 python 中 slice 的概念:
1 2 3 4 5 6 7 8 a = [1 , 2 , 3 , 4 , 5 ] b = a[:] b = a[::-1 ] b = a[::2 ] a[:0 ] = [1 , 2 ] del a[:3 ]del a[::2 ]
这里我们将实现一个自定义的不可变切片序列类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Group (object ): """ 可切片的类 """ def __init__ (self, group_name, company_name, staffs ): self.group_name = group_name self.company_name = company_name self.staffs = staffs def __reversed__ (self ): cls = type (self) reversed_staffs = self.staffs.reverse() return cls(group_name=self.group_name, company_name=self.company_name, staffs=reversed_staffs) def __getitem__ (self, item ): cls = type (self) if isinstance (item, int ): return cls(group_name=self.group_name, company_name=self.company_name, staffs=[self.staffs[item]]) elif isinstance (item, slice ): return cls(group_name=self.group_name, company_name=self.company_name, staffs=self.staffs[item]) else : raise IndexError def __len__ (self ): return len (self.staffs) def __iter__ (self ): return iter (self.staffs) def __contains__ (self, item ): if item in self.staffs: return True else : return False if __name__ == '__main__' : group = Group(company_name="1" , group_name="1.1" , staffs=[1 , 2 ]) var = group[1 :2 ] print (var) print (group[0 ].staffs) print (len (group))
上述代码中,按照序列类的协议实现了一个自定义的序列,借助了内置的序列类 list
bisect 模块介绍 模块介绍:https://docs.python.org/zh-cn/3/library/bisect.html
bisect
模块是Python标准库中的一个模块,提供了对已排序列表的二分查找和插入操作的支持。它基于二分查找算法,在有序序列中进行高效的查找和插入。
bisect
模块提供了以下常用的函数:
bisect_left(a, x, lo=0, hi=len(a))
: 返回在已排序列表 a
中将元素 x
插入的位置(左边界)。 如果 x
已经存在于列表中,那么返回其最左边的位置。可选参数 lo
和 hi
可用于指定查找范围。
bisect_right(a, x, lo=0, hi=len(a))
: 返回在已排序列表 a
中将元素 x
插入的位置(右边界)。 如果 x
已经存在于列表中,那么返回其最右边的位置。可选参数 lo
和 hi
可用于指定查找范围。
bisect(a, x, lo=0, hi=len(a))
: bisect_right
的别名函数,返回在已排序列表 a
中将元素 x
插入的位置(右边界)。
insort_left(a, x, lo=0, hi=len(a))
: 将元素 x
按照有序方式插入到已排序列表 a
中的正确位置(左边界)。 可选参数 lo
和 hi
可用于指定插入范围。
insort_right(a, x, lo=0, hi=len(a))
: 将元素 x
按照有序方式插入到已排序列表 a
中的正确位置(右边界)。 可选参数 lo
和 hi
可用于指定插入范围。
bisect
模块的函数可用于在已排序的序列中进行高效的查找和插入操作 ,例如在有序列表中快速找到插入点,或者将元素按照正确的顺序插入到列表中。这在需要处理大量数据的应用中特别有用,如搜索、排名、数据统计等场景。
1 2 3 4 5 6 7 8 9 import bisectinter_li = [] bisect.insort(inter_li, 3 ) bisect.insort(inter_li, 2 ) bisect.insort(inter_li, 5 ) bisect.insort(inter_li, 6 ) bisect.insort(inter_li, 0 ) bisect.insort(inter_li, 2 )
列表的使用场景&&其他容器对象介绍 什么时候我们不该使用列表?
arrayC 语言中的数组,连续的数组空间,存储的数据类型是一致的 官方参考文档:https://docs.python.org/zh-cn/3/library/array.html 常用的类型映射:1 2 3 4 5 6 7 8 9 10 11 12 13 14 Type code C Type Minimum size in bytes 'b' signed integer 1 'B' unsigned integer 1 'u' Unicode character 2 (see note)'h' signed integer 2 'H' unsigned integer 2 'i' signed integer 2 'I' unsigned integer 2 'l' signed integer 4 'L' unsigned integer 4 'q' signed integer 8 (see note)'Q' unsigned integer 8 (see note)'f' floating point 4 'd' floating point 8
1 2 3 4 5 6 7 import arrayif __name__ == '__main__' : my_arr = array.array("i" ) my_arr.append(1 ) print (my_arr)
更为详细的用法可以参考官方文档