先回顾一下 Python 里的序列分类,按照存储类型可以分为容器序列和扁平序列
容器序列可以存放不同类型的数据。即可以存放任意类型对象的引用。比如 list, tuple, deque
扁平序列只能容纳一种类型,存放的是值而不是引用,实际是占据一段连续的内存空间,但只能存放诸如字符、字节和数值等基础类型数据。比如 str,bytes,bytearray,array.array
按照存储内容是否可变,又可分类成可变序列与不可变序列。前者包括 list,deque,bytearray,array。后者包括 str,tuple,bytes
不可变序列类型的实现可以参照 collections.abc.Sequence
,继承 Reversible
和 Collection
两个类,需要实现 __getitem__
和 __len__
两个方法。而 MutableSequence 是继承 Sequence 后又通过 mixin 添加了若干方法
import collections.abc as abc
help(abc.Sequence)
help(abc.MutableSequence)
对于序列类型,拼接和添加是最常见的操作,但仔细追究还另有天地。+
执行的是运算,会对两个算子进行格式审查,+=
不同,它是通过 MutableSequence 中的 __iadd__
实现的,方法内部调用了 extend
方法,所以 +=
和调用 extend
一样,都接一个可迭代对象做参数,执行就地变换。加法则会返回运算结果
one_lst = [1,2]
rst_lst = one_lst + [3, 4] # 加法有类型检查,左右类型要一致
rst_lst
one_lst += [3, 4]
one_lst
one_lst += (5, 6) # 可迭代对象都能参与计算,效果等同于 one_lst.extend(value)
one_lst
one_lst.extend((7,8))
one_lst
注意这里不要和 append
方法弄混,append(object)
是在序列末尾添加一个新对象,而不是执行序列合并操作
one_lst.append([9,10])
one_lst
Python 中的序列,比如 list,一大特色且高频使用的功能是切片,无论是取值,反转,添加,删除,在不谈可读性时切片实际都能实现。这么灵活的功能在实现上只需要完成 __getitem__
方法,就能对对象进行切片操作了
class MySlice:
def __init__(self,val_lst):
self.vals = val_lst
def __getitem__(self, item):
return self.vals[item]
my_slice = MySlice([1,2,3,4,5,6])
my_slice[::-1]
此时结果看似没问题,但实际上述实现中 self.vals[item]
是借助 list
完成了切片,返回的数据类型是 list
不符合预期,对于一个序列,切片后应该还是本身才对。
rst_slice = my_slice[::-1]
type(rst_slice)
问题的关键在于传入的参数 item
上,它是 Python 内置类型 slice
的一个实例,当执行切片操作时,操作内容就被封装在其中,但却没有返回类型检查。所以经验上,对于序列类型的切片操作,需要自行实现操作类型判断,是取某个位置的值,还是切片,进而返回相应结果。
import numbers
class Group:
def __init__(self, group_name, company_name, staffs):
self.group_name = group_name
self.company_name = company_name
self.staffs = staffs
def __getitem__(self, item):
clss = type(self)
if isinstance(item, slice): # sequence[::]
return clss(self.group_name,self.company_name,self.staffs[item])
elif isinstance(item, numbers.Integral): # sequence[index]
return clss(self.group_name,self.company_name, [self.staffs[item]])
# 实现自定义序列类的其他方法
def __len__(self):
return len(self.staffs)
def __reversed__(self):
self.staffs.reverse()
def __iter__(self):
return iter(self.staffs)
def __contains__(self, item):
return True if item in self.staffs else False
group = Group(company_name='Bilibili',group_name='anime',staffs=['A','B','C','D'])
group[2:].__dict__
group[1].__dict__
实际开发中排序是永恒的主题,对于序列类型尤甚。如果自己维护序列可能会不停的重复造轮子,效率低下。Python 对此提供了 bisect
模块专门用于维护有序序列,默认升序。主要方法有 insort
,bisect
通过二分法,前者负责插入,后者负责寻找目标位置。为了一些特殊需求,模块还提供了 insort_right
,insort_left
,bisect_right
,bisect_left
方法来确认目标的前后近邻位置
insort
和 bisect
等价于 insort_right
和 bisect_right
import bisect
from collections import deque
sorted_seq_1 = []
bisect.insort(sorted_seq_1, 4)
bisect.insort(sorted_seq_1, 5)
bisect.insort(sorted_seq_1, 3)
bisect.insort(sorted_seq_1, 2)
print(sorted_seq_1)
sorted_seq_2 = deque()
bisect.insort(sorted_seq_2, 8)
bisect.insort(sorted_seq_2, 7)
bisect.insort(sorted_seq_2, 9)
bisect.insort(sorted_seq_2, 6)
print(sorted_seq_2)
bisect.bisect(sorted_seq_1, 4)