本小节介绍序列分类、序列的协议、+和=+的区别、extend和append区别、如何实现可切片的对象、用bisect维护一个升序序列。

1.序列类型分类

序列类型分类可以从两个维度进行分类。第一个维度序列存储数据类型,第二个维度序列是否能被修改

ssl

分类名称描述类型
容器序列容器序列能存放不同类型的数据。容器序列存放的死它们所包含的任意类型的对象的引用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]

python3.7 进阶

python 一切皆对象 python 魔法函数 python 类和对象 python Mixin python 自定义序列类 python dict python 对象引用、可变性和垃圾回收 python 元类编程