本小节介绍序列分类、序列的协议、+和=+的区别、extend和append区别、如何实现可切片的对象、用bisect维护一个升序序列。
1.序列类型分类
序列类型分类可以从两个维度进行分类。第一个维度序列存储数据类型
,第二个维度序列是否能被修改
。
分类名称 | 描述 | 类型 |
---|
容器序列 | 容器序列能存放不同类型的数据。容器序列存放的死它们所包含的任意类型的对象的引用 | list、tuple、deque、collections |
扁平序列 | 扁平序列只能容纳一种类型。扁平序列存放的是值而不是引用。换句话说,扁平序列其实是一段连续的内存空间。由此可见,扁平序列其实更加紧凑,但是它里面只能存放诸如字符,字节和数值这种基础类型 | str、bytes、bytearray、memoryview、array.array |
可变序列 | 存放的值可以被改变 | list,bytearray,array.array,collection.deque,memoryview |
不可变序列 | 存放的值不可以被改变,变量在初始化时就确定了 | tuple,str,bytes |
2.序列协议
序列协议这里主要根据abc
(抽象基类)的继承关系来理解。
import collections.abc
# abc模块
__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",
]
collections.abc 工具类中有Sequence
(不可变序列)和MutableSequence
(可变序列)
# Sequence 源码
class Sequence(Reversible, Collection):
...
...
# Collection 源码
class Collection(Sized, Iterable, Container):
...
...
Sequence
继承Reversible
(数据反转)和Collection
,Collection继承Sized
(获取长度)、Iterable
(可迭代,可以用for遍历)、Container
(可以用in来判断是否在序列里)。Sequence
实现上面接口的方法后就构成了序列协议。
# MutableSequence 源码
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().
"""
...
MutableSequence可变序列协议是在Sequence
基础上实现__getitem__
、__setitem__
、__delitem__
等方法。
3.+和+=区别
li = [1,2,3]
k = li + [5,6]
print(k)
>>> [1,2,3,5,6]
li += [5,6]
print(li)
>>> [1,2,3,5,6]
将上面代码修改为
li = [1,2,3]
+ k = li + (5,6)
print(k)
>>> TypeError: can only concatenate list (not "tuple") to list
+ li += (5,6)
print(li)
>>> [1,2,3,5,6]
运行上面代码,控制台报错了。这是因为+
两边是同类型的,所以list类型和tuple类型不能直接相+
。那为什么+=
就可以呢?这是因为+=
在序列中调用的是extend
方法,extend方法中接收的是iterable
(可迭代)类型数据。
4.extend和append区别
li = [1,2,3]
li.extend((5,6))
print(li)
>>> [1,2,3,5,6]
k = [1,2,3]
k.append((5,6))
print(k)
>>> [1,2,3,(5,6)]
append
是将整个数据作为一个值添加到list中,extend
将整个数据中每一个值添加都list中去。这一点需要注意。
5.实现可切片对象
上面序列协议
讲过序列之间的继承关系,python中是根据协议来编程的,简洁的来说就是实现对应的魔法函数。上面讲过一个Sequence
继承Reversible
(数据反转)和Collection
,Collection继承Sized
(获取长度)、Iterable
(可迭代,可以用for遍历)、Container
(可以用in来判断是否在序列里)。Sequence
实现上面接口的方法后就构成了序列协议。所以我们实现对应的魔法函数就可以实现类的切片操作
import numbers
class Group:
def __init__(self, group, company, staffs):
self.group = group
self.company = company
self.staffs = staffs
def __reversed__(self):
self.staffs.reverse()
def __getitem__(self, item):
cls = type(self)
if isinstance(item, slice):
return cls(group=self.group, company=self.company, staffs=self.staffs[item])
elif isinstance(item, numbers.Integral):
return cls(group=self.group, company=self.company, 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 = ['s1', 's2', 's3']
group = Group('test', 'baidu', staffs)
sub = group[:2]
# __getitem__ 协议
print(sub.staffs)
# __len__ 协议
print(len(sub))
# __iter__ 协议
for s in sub:
print(s)
# __contains__ 协议
if 's1' in sub:
print("yes")
# __reversed__ 协议
reversed(sub)
6.bisect升序
bisect
模块维护一个升序的序列。
import bisect
li = []
bisect.insort(li, 5)
bisect.insort(li, 4)
bisect.insort(li, 7)
bisect.insort(li, 1)
bisect.insort(li, 2)
print(li)
>>> [1, 2, 4, 5, 7]