04 python自定义序列类
python中的序列分类
序列类型的分类:
① 容器序列:list,tuple,deque(可以防任意的类型的容器)
② 扁平序列:str,bytes,bytearray,array.array(可以使用 for循环遍历的)
③ 可变序列:list,deque,bytearray,array
④ 不可变:str,tuple,bytes
python中序列类型的abc继承关系
from collections import abc
查看abc的源码
1 2 3 4 5 6 7 8 9 10 | __all__ = [ "Awaitable" , "Coroutine" , "AsyncIterable" , "AsyncIterator" , "AsyncGenerator" , "Hashable" , "Iterable" , "Iterator" , "Generator" , "Reversible" , "Sized" , "Container" , "Callable" , "Collection" , "Set" , "MutableSet" , "Mapping" , "MutableMapping" , "MappingView" , "KeysView" , "ItemsView" , "ValuesView" , "Sequence" , "MutableSequence" , "ByteString" , ] |
Sequence不可变序列
1 2 3 4 5 6 7 8 9 | class Sequence(Reversible, Collection): """All the operations on a read-only sequence. Concrete subclasses must override __new__ or __init__, __getitem__, and __len__. """ __slots__ = () |
可以看到 Sequence 继承 Reversible和Collection
Reversible可以实现反转
1 |
1 2 3 4 5 6 7 8 | class Reversible(Iterable): __slots__ = () @abstractmethod def __reversed__( self ): while False : yield None |
Collection 继承 Sized, Iterable, Container
1 2 3 | class Collection(Sized, Iterable, Container): pass |
Sized 可以使用len()查看对象的长度
1 2 3 4 5 6 7 | class Sized(metaclass = ABCMeta): __slots__ = () @abstractmethod def __len__( self ): return 0 |
Iterable 可以是一个对象变成可迭代对象
1 2 3 4 5 6 7 8 | class Iterable(metaclass = ABCMeta): __slots__ = () @abstractmethod def __iter__( self ): while False : yield None |
Container 可以是if in 判断一个属性是否在对象内
1 2 3 4 5 6 7 | class Container(metaclass = ABCMeta): __slots__ = () @abstractmethod def __contains__( self , x): return False |
MutableSequence可变序列
继承 Sequence,自己内部也封装了增删改查的方法
class MutableSequence(Sequence): __slots__ = () """All the operations on a read-write sequence. Concrete subclasses must provide __new__ or __init__, __getitem__, __setitem__, __delitem__, __len__, and insert(). """ @abstractmethod def __setitem__(self, index, value): raise IndexError @abstractmethod def __delitem__(self, index): raise IndexError @abstractmethod def insert(self, index, value): 'S.insert(index, value) -- insert value before index' raise IndexError def append(self, value): 'S.append(value) -- append value to the end of the sequence' self.insert(len(self), value) def clear(self): 'S.clear() -> None -- remove all items from S' try: while True: self.pop() except IndexError: pass def reverse(self): 'S.reverse() -- reverse *IN PLACE*' n = len(self) for i in range(n//2): self[i], self[n-i-1] = self[n-i-1], self[i] def extend(self, values): 'S.extend(iterable) -- extend sequence by appending elements from the iterable' for v in values: self.append(v) def pop(self, index=-1): '''S.pop([index]) -> item -- remove and return item at index (default last). Raise IndexError if list is empty or index is out of range. ''' v = self[index] del self[index] return v def remove(self, value): '''S.remove(value) -- remove first occurrence of value. Raise ValueError if the value is not present. ''' del self[self.index(value)] def __iadd__(self, values): self.extend(values) return self
Sequence 继承 Reversible, Collection 功能和不可变序列一样
1 2 | class Sequence(Reversible, Collection): pass |
list中+,+= 和extend方法区别
’+’两边必须为统一类型的
1 2 3 | a = [ 1 , 2 ] b = a + [ 3 , 4 ] # 这样是可以的=>b=[1,2,3,4] b = a + ( 3 , 4 ) # 这样是不可行的,’+’两边必须为统一类型的 |
+= 只要是可迭代对象就可以想加
1 2 3 | c = [ 3 , 4 ] c + = ( 1 , 2 ) # 这样也是可以的 =>c=[3,4,1,2] c + = ‘hello’ #这样也是可以的 =>c=[3,4,’h’,’e’,’l’,’l’,’o’] |
内部实现
会调用__iadd__
1 2 3 | def __iadd__( self , values): self .extend(values) return self |
extend 遍历添加
1 2 3 4 | def extend( self , values): 'S.extend(iterable) -- extend sequence by appending elements from the iterable' for v in values: self .append(v) |
extend 直接作用域本身,没返回值
1 2 3 4 | d = [ 5 , 6 ] a.entend(d) #这样是可以的,a=[1,2,5,6] d = ( 5 , 6 ) a.extend(d) #这样也是可以的,a=[1,2,5,6] |
实现可切片的对象
python 帮我们实现的切片
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 | # 模式[start:end:step] """ 其中,第一个数字start表示切片开始位置,默认为0; 第二个数字end表示切片截止(但不包含)位置(默认为列表长度); 第三个数字step表示切片的步长(默认为1)。 当start为0时可以省略,当end为列表长度时可以省略, 当step为1时可以省略,并且省略步长时可以同时省略最后一个冒号。 另外,当step为负整数时,表示反向切片,这时start应该比end的值要大才行。 """ aList = [ 3 , 4 , 5 , 6 , 7 , 9 , 11 , 13 , 15 , 17 ] print (aList[::]) # 返回包含原列表中所有元素的新列表 print (aList[:: - 1 ]) # 返回包含原列表中所有元素的逆序列表 print (aList[:: 2 ]) # 隔一个取一个,获取偶数位置的元素 print (aList[ 1 :: 2 ]) # 隔一个取一个,获取奇数位置的元素 print (aList[ 3 : 6 ]) # 指定切片的开始和结束位置 aList[ 0 : 100 ] # 切片结束位置大于列表长度时,从列表尾部截断 aList[ 100 :] # 切片开始位置大于列表长度时,返回空列表 aList[ len (aList):] = [ 9 ] # 在列表尾部增加元素 aList[: 0 ] = [ 1 , 2 ] # 在列表头部插入元素 aList[ 3 : 3 ] = [ 4 ] # 在列表中间位置插入元素 aList[: 3 ] = [ 1 , 2 ] # 替换列表元素,等号两边的列表长度相等 aList[ 3 :] = [ 4 , 5 , 6 ] # 等号两边的列表长度也可以不相等 aList[:: 2 ] = [ 0 ] * 3 # 隔一个修改一个 print (aList) aList[:: 2 ] = [ 'a' , 'b' , 'c' ] # 隔一个修改一个 aList[:: 2 ] = [ 1 , 2 ] # 左侧切片不连续,等号两边列表长度必须相等 aList[: 3 ] = [] # 删除列表中前3个元素 del aList[: 3 ] # 切片元素连续 del aList[:: 2 ] # 切片元素不连续,隔一个删一个 |
可以看出python的切片功能是相当强大的,那我们如何去实现呢?
在这里我实现的是不可变序列 ,查看abc中 Sequence 源码按照python协议实现其内部的魔法函数即可
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 | 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 __reversed__( self ): self .staffs.reverse() def __getitem__( self , item): "返回一个对象" cls = type ( self ) if isinstance (item, slice ): return cls (group_name = self .group_name, company_name = self .company_name, staffs = self .staffs[item]) elif isinstance (item, numbers.Integral): return cls (group_name = self .group_name, company_name = self .company_name, staffs = [ self .staffs[item]]) 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 staffs = [ "bobby1" , "imooc" , "bobby2" , "bobby3" ] group = Group(company_name = "imooc" , group_name = "user" , staffs = staffs) print (group[ 0 ]) # __getitem__ 中的item 为 int 0 print (group[: 2 ]) # __getitem__ 中的item 为 slice 类型 slice(None, 2, None) reversed (group) for user in group: print (user) |
输出结果如下
bisect维护已排序序列
我们可以使用bisect 帮助我们在不断的向序列中插入数据是,而我们的列表还是一个已排序的
1 2 3 4 5 6 7 8 9 10 11 12 | import bisect #用来处理已排序的序列,用来维持已排序的序列, 升序 #二分查找 inter_list = [] bisect.insort(inter_list, 3 ) bisect.insort(inter_list, 2 ) bisect.insort(inter_list, 5 ) bisect.insort(inter_list, 1 ) bisect.insort(inter_list, 6 ) print (inter_list) |
输出结果如下
也可以使用双端队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import bisect from collections import deque #用来处理已排序的序列,用来维持已排序的序列, 升序 #二分查找 inter_list = deque() bisect.insort(inter_list, 3 ) bisect.insort(inter_list, 2 ) bisect.insort(inter_list, 5 ) bisect.insort(inter_list, 1 ) bisect.insort(inter_list, 6 ) #学习成绩 print (inter_list) |
输出结果
什么时候我们不该使用列表
list 和array的区别
1 list中可以存放任意的数据类型,array中只能存放单一的数据类型。
2 如果是单一的数据类型array是比list更加高校
基本使用
1 2 3 4 5 6 7 8 9 | # array, deque # 数组 import array #array和list的一个重要区别, array只能存放指定的数据类型 my_array = array.array( "i" ) # 只能存放int类型 my_array.append( 1 ) print (my_array) my_array.append( "abc" ) # 会报错 |
输出结果如下
列表推导式、生成器表达式、字典推导式
列表推导式
1 2 | qu_list = [item * item for item in range ( 6 )] print (qu_list) |
1 2 3 4 5 6 7 | #笛卡尔积 int_list1 = [ 1 , 2 ] int_list2 = [ 3 , 4 ] qu_list = [(first, second) for first in int_list1 for second in int_list2] print (qu_list) |
1 2 3 4 5 | def trs_str(item): return str (item) qu_list = [trs_str(item) for item in range ( 6 )] print (qu_list) |
字典推导式
1 2 3 4 5 | def process_item(item): return str (item) int_dict = {process_item(item):item for item in range ( 5 )} print (int_dict) |
生成器表达式
1 2 3 4 5 6 7 | my_dict = { "key1" : "bobby1" , "key2" : "bobby2" } res = ((key, value) for key, value in my_dict.items()) print (res) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理