python定义序列_Python中自定义序列类

2ff34e647e2e3cdfd8dca593e17d9b0a.png

这篇文章我们会了解到Python的序列协议是什么,以及通过序列协议使类或者实例转变为序列。

Python中的内置序列分类

我们按照两个维度来区分序列

第一个维度是按照序列存贮的数据类型,分为容器序列和扁平序列1

2容器序列

list, tuple, deque1

2扁平序列

str, bytes, bytearray, array.array

容器序列里面可以放任意类型的数据。扁平序列的数据类型一致。

注意:array和list的一个重要区别, array只能存放指定的数据类型

第二个维度是按照序列是否可变,分为可变序列和不可变序列1

2可变序列

list, deque, bytearray, array1

2不可变

str, tuple, bytes

Python中序列类型的abc继承关系

这一小节我们将通过abc继承关系来讲下序列协议

跟容器相关的数据结构的抽象基类都存在_collections_abc.py模块下。1"Sequence"(不可变序列), "MutableSequence"(可变序列),

我们看下不可变序列(Sequence)的源码是由哪些抽象函数协议组成的。1

2

3

4

5

6

7class (Reversible, Collection):

"""All the operations on a read-only sequence.

Concrete subclasses must override __new__ or __init__,

__getitem__, and __len__.

"""

Sequence继承了Reversible(用于翻转)和Collection。

我们再看看Collection的源码。1

2

3

4

5

6

7

8

9class Collection(Sized, Iterable, Container):

__slots__ = ()

def __subclasshook__(cls, C):

if cls is Collection:

return _check_methods(C, "__len__", "__iter__", "__contains__")

return NotImplemented

Collection 又分别继承Sized, Iterable, Container。我们看下这三个类的源码1

2

3

4

5

6

7

8

9

10

11

12

13class Sized(metaclass=ABCMeta):

__slots__ = ()

@abstractmethod

def __len__(self):

return 0

def __subclasshook__(cls, C):

if cls is Sized:

return _check_methods(C, "__len__")

return NotImplemented

Sized实现了__len__使我们的序列具有长度。1

2

3

4

5

6

7

8

9

10

11

12

13

14class Iterable(metaclass=ABCMeta):

__slots__ = ()

@abstractmethod

def __iter__(self):

while False:

yield None

def __subclasshook__(cls, C):

if cls is Iterable:

return _check_methods(C, "__iter__")

return NotImplemented

Iterable实现了__iter__使我们的序列可以迭代(for 操作)1

2

3

4

5

6

7

8

9

10

11

12

13class Container(metaclass=ABCMeta):

__slots__ = ()

@abstractmethod

def __contains__(self, x):

return False

def __subclasshook__(cls, C):

if cls is Container:

return _check_methods(C, "__contains__")

return NotImplemented

Container实现了__contains__使我们可以使用 is in 判断是否存在序列中。

通过上述的魔法函数组成了构成不可变序列的协议。

对于可变序列MutableSequence,作为不可变序列Sequence的子类,我们看看它的源码多实现了哪些魔法函数。1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18class 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

我们看到最主要的是新增了__setitem__用于赋值,__delitem__用于删除值。这两个魔法函数。

如果我们想自定义一些序列类,只需要实现上述魔法函数(协议)即可。

序列的+、+=和extend的区别

我们看下下面代码1

2

3

4

5

6a = [1, 2]

c = a + [3, 4]

a += (3, 4)

a += [3, 4]

对于 + 两边的数据类型必须一致,而 += 只需要是序列类型即可。

为什么 +=只要是序列就可以呢?

我们看看+=的实现源码:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18class MutableSequence(Sequence):

__slots__ = ()

"""All the operations on a read-write sequence.

Concrete subclasses must provide __new__ or __init__,

__getitem__, __setitem__, __delitem__, __len__, and insert().

"""

def extend(self, values):

'S.extend(iterable) -- extend sequence by appending elements from the iterable'

for v in values:

self.append(v)

def __iadd__(self, values):

self.extend(values)

return self

在可变序列MutableSequence中的__iadd__就是实现 +=操作的,我们看到中间有调用

extend,我们看看extend函数有要求的是可迭代类型。

对于extend:1

2

3

4

5

6a.extend(range(3))

def extend(self, iterable): # real signature unknown; restored from __doc__

""" L.extend(iterable) -> None -- extend list by appending elements from the iterable """

pass

我们看到extend内置源码实现原理接收一个可迭代对象。

实现可切片的对象

下面是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# 模式[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]

aList[::] # 返回包含原列表中所有元素的新列表

aList[::-1] # 返回包含原列表中所有元素的逆序列表

aList[::2] # 隔一个取一个,获取偶数位置的元素

aList[1::2] # 隔一个取一个,获取奇数位置的元素

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 # 隔一个修改一个

aList[::2] = ['a', 'b', 'c'] # 隔一个修改一个

aList[::2] = [1, 2] # 左侧切片不连续,等号两边列表长度必须相等

aList[:3] = [] # 删除列表中前3个元素

del aList[:3] # 切片元素连续

del aList[::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

42

43

44

45import numbers

class Group:

#支持切片操作

# staffs 是一个list以便于实现对数据的管理

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):

# 切片主要的实现函数

# item有两类

# 当我们使用[:2]这种方式的时候item是切片类 item = {slice} slice(None, 2, None)

# 当使用[2]这种方式的时候item是一个int类型 item = {int} 2

# 下面返回的仍然是一个Group方便切片之后仍然可以切片

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):

# 使用 is in

if item in self.staffs:

return True

else:

return False

staffs = ["staff1", "staff2", "staff3", "staff4"]

group = Group(company_name="alibaba", group_name="user", staffs=staffs)

bisect维护已排序序列

注意:一定要是一个已排序的序列

我们先看下这个模块的源码结构

006tNc79gy1fsao4ht1cxj31gs0wwwmx.jpg

其中的insort为插入数据是insort_right的缩写,bisect为查询插入位置是bisect_right的缩写。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

37In [11]: import bisect

In [12]: inter_list = []

# 我们使用insort插入数据 排序查找规则是二分法

In [13]: bisect.insort(inter_list, 3)

In [14]: bisect.insort(inter_list, 4)

In [15]: bisect.insort(inter_list, 1)

In [16]: bisect.insort(inter_list, 2)

In [17]: bisect.insort(inter_list, 5)

In [18]: inter_list

Out[18]: [1, 2, 3, 4, 5]

# 我们使用bisect查找应该插入的位置

# bisect_right表示在查找到重复值的右侧插入

In [19]: bisect.bisect(inter_list, 3)

Out[19]: 3

# bisect_left表示在查找到重复值的左侧插入

In [20]: bisect.bisect_left(inter_list, 3)

Out[20]: 2

# 对应的插入

In [21]: bisect.insort(inter_list, 3)

In [22]: inter_list

Out[22]: [1, 2, 3, 3, 4, 5]

In [23]: bisect.insort_left(inter_list, 3)

In [24]: inter_list

Out[24]: [1, 2, 3, 3, 3, 4, 5]

什么时候不该使用列表

有时对于使用list对性能有很大影响或者,我们可以考虑使用其他序列如array, deque。1

2

3

4

5

6import array

# array和list的一个重要区别, array只能存放指定的数据类型

# i 表示整型

my_array = array.array("i")

my_array.append(1)

array支持的数据类型查看 array

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值