序列类型的分类

按照序列存储数据的类型

  • 容器序列:list, tuple, deque
  • 扁平序列:str, bytes, array.array, bytearray

    按照序列是否可变

  • 可变序列:list, deque, array.array, bytearray

  • 不可变序列:str, tuple, bytes

    序列的abc继承关系

    引言

  • collections.abc模块中有很多内置的抽象基类

  • 序列主要和两个抽象基类有关:SequenceMutableSequence

    细节

    Sequence

    1. """
    2. Sequence 抽象基类
    3. 它继承了Sized Iterable Contain 这三个抽象基类
    4. """
    5. class Sequence(Sized, Iterable, Container):
    6. """All the operations on a read-only sequence.
    7. Concrete subclasses must override __new__ or __init__,
    8. __getitem__, and __len__.
    9. """
    10. __slots__ = ()
    11. @abstractmethod
    12. def __getitem__(self, index):
    13. raise IndexError
    14. def __iter__(self):
    15. i = 0
    16. try:
    17. while True:
    18. v = self[i]
    19. yield v
    20. i += 1
    21. except IndexError:
    22. return
    23. def __contains__(self, value):
    24. for v in self:
    25. if v == value:
    26. return True
    27. return False
    28. def __reversed__(self): # 该魔法函数使得序列类型可以反转
    29. for i in reversed(range(len(self))):
    30. yield self[i]
  1. class Sized(metaclass=ABCMeta):
  2. __slots__ = ()
  3. @abstractmethod
  4. def __len__(self): # 这个魔法函数使得序列类型可以使用 len()获得长度
  5. return 0
  6. @classmethod
  7. def __subclasshook__(cls, C):
  8. if cls is Sized:
  9. if any("__len__" in B.__dict__ for B in C.__mro__):
  10. return True
  11. return NotImplemented
  1. class Iterable(metaclass=ABCMeta):
  2. __slots__ = ()
  3. @abstractmethod
  4. def __iter__(self): # 这个魔法函数使得序列类型可以进行 for 循环
  5. while False:
  6. yield None
  7. @classmethod
  8. def __subclasshook__(cls, C):
  9. if cls is Iterable:
  10. if any("__iter__" in B.__dict__ for B in C.__mro__):
  11. return True
  12. return NotImplemented
  1. class Container(metaclass=ABCMeta):
  2. __slots__ = ()
  3. @abstractmethod
  4. def __contains__(self, x): # 这个魔法函数使得序列类型可以使用 in 操作符
  5. return False
  6. @classmethod
  7. def __subclasshook__(cls, C):
  8. if cls is Container:
  9. if any("__contains__" in B.__dict__ for B in C.__mro__):
  10. return True
  11. return NotImplemented

MutableSequence

  1. class MutableSequence(Sequence):
  2. __slots__ = ()
  3. """All the operations on a read-write sequence.
  4. Concrete subclasses must provide __new__ or __init__,
  5. __getitem__, __setitem__, __delitem__, __len__, and insert().
  6. """
  7. @abstractmethod
  8. def __setitem__(self, index, value):
  9. raise IndexError
  10. @abstractmethod
  11. def __delitem__(self, index):
  12. raise IndexError
  13. def __iadd__(self, values): # 使得序列可以使用 += 运算符号进行计算
  14. self.extend(values)
  15. return self
  16. # ......只列出几个有代表意义的魔法函数......
  17. # 这些魔法函数使得序列类型可以改变

序列的+、+=和extend

引言

  • 对于可变序列,我们有三种方式来扩充原始序列
  • 三种方式有原理效率上的不同,我们要根据实际情况选择合适的方式来使用

    使用范例

    ```python “”” 普通的加法会在内存中产生一个新的序列对象 时间 空间开销相对比较大 必须要是类型完全一致才能够使用这种合并方式 “”” a, b, c = [1, 2], [3, 4], (5, 6) d = a + b # [1, 2] + [3, 4] print(d)

result:

[1, 2, 3, 4]

d = a + c # [1, 2] + (5, 6) print(d)

result:

TypeError: can only concatenate list (not “tuple”) to list

  1. ```python
  2. """
  3. += 可以理解为原地加,单纯地在原对象上进行修改,效率比 + 要高很多
  4. 这个功能背后有一个魔法函数,__iadd__(self, values)
  5. 上一小节中有这个函数具体的实现,我们可以看到,它是调用了 extend()方法
  6. 所以本质上,它和调用 extend()函数产生的效果是一模一样的
  7. """
  8. a, b, c = [1, 2], [3, 4], (5, 6)
  9. a += b
  10. print(a)
  11. # result:
  12. # [1, 2, 3, 4]
  13. a += c
  14. print(a)
  15. # result:
  16. # [1, 2, 3, 4, 5, 6]

鸭子类型那一小节,我们分析了extend()函数的参数要求,只要是可迭代类型都能够作为参数扩充列表。
+=操作本质上就是调用 extend() 函数,所以能将列表元组进行合并也就不奇怪了,因为他们本质上都是可迭代的类型。

实现可切片的对象

引言

切片介绍

  1. # 使用模式:[start:end:step]
  2. """
  3. start: 切片开始的位置,默认是 0
  4. end: 切片截止(不包括)的位置,默认是列表长度
  5. step: 切片的步长,默认是 1
  6. start和end为默认值时可以省略不写,step为默认值时可以连同最后的冒号一起省略
  7. step为负数时,表示反向切片,此时需满足: start > end
  8. """

切片举例

  1. li = [8, 4, 3, 2, 1, 7]
  2. li[::] # 返回原序列
  3. li[::-1] # 返回原序列的逆序列表
  4. li[::2] # 隔一个取一个,所有偶数位置
  5. li[1::2] # 隔一个取一个,所有奇数位置
  6. li[3:6] # 取[3, 6)区间内的所有元素,左包含,右不包含
  7. li[0:666] # 结束位置大于列表长度,从尾部截断,返回原列表
  8. li[666:] # 开始位置大于列表长度,直接返回空列表
  9. li = [6, 6]
  10. li[:0] = [1, 2] # 列表头部插入元素 [1, 2, 6, 6]
  11. li[1:1] = [1, 1] # 列表的某一位置插入元素 [6, 1, 1, 6]
  12. li[:1] = [1, 1] # 替换列表元素 [1, 1, 6]
  13. li[1:] = [1, 1] # 替换列表元素 [6, 1, 1]

使用案例

  1. class Group:
  2. def __init__(self, group_name, staffs):
  3. self.group_name = group_name
  4. self.staffs = staffs
  5. def __reversed__(self): # reversed(group)
  6. self.staffs.reverse()
  7. """
  8. 在使用切片和索引访问操作时,相关的参数会传递到 __getitem__(self, item) 中
  9. 我们通常希望,切片返回的对象和原对象是同一种类型
  10. 使用切片的话,传入的item是一个slice类型
  11. 使用索引访问,传入的item是一个int类型
  12. """
  13. def __getitem__(self, item): # 对象可切片的关键
  14. cls = type(self) # 获得该实例的类型
  15. if isinstance(item, slice):
  16. return cls(self.group_name, self.staffs[item]) # 委托给列表实现切片
  17. elif isinstance(item, int):
  18. return self.staffs[item]
  19. def __len__(self):
  20. return len(self.staffs)
  21. def __iter__(self):
  22. return iter(self.staffs) # 后面会详细讲解
  23. def __contains__(self, item):
  24. return item in self.staffs # 同样委托给列表的in操作来实现
  25. def __str__(self):
  26. return "name:{name}\nstaffs:{staffs}".format(name=self.group_name, staffs=self.staffs)
  27. staffs = ["MetaTian0", "MetaTian1", "MetaTian2"]
  28. group = Group("HIT", staffs)
  29. print(group[1]) # 索引访问
  30. print(group[0:2]) # 切片
  31. reversed(group) # 反转
  32. print(group)
  33. print(len(group)) # 长度
  34. print("MetaTian0" in group, "MetaTian6" in group) # in 操作符
  35. # resutl:
  36. # MetaTian1
  37. # name:HIT; staffs:['MetaTian0', 'MetaTian1']
  38. # name:HIT; staffs:['MetaTian2', 'MetaTian1', 'MetaTian0']
  39. # 3
  40. # True False

bisect模块

引言

  • 如果我们在一个有序序列中需要增加一个元素,但任然要维持序列的有序性,可以使用append()添加元素,再调用sort()来重新排序。
  • bisect模块用来维持已排序序列(升序)的顺序,效率更高。
  • 两个关键函数insort()bisect

    使用案例

    ```python import bisect

int_list = [] “”” 在插入过程中维护序列的有序性 “”” bisect.insort(int_list, 2) bisect.insort(int_list, 6) bisect.insort(int_list, 1) bisect.insort(int_list, 3) bisect.insort(int_list, 5)

print(int_list)

result:

[1, 2, 3, 5, 6]

“”” 查询一个元素在序列中应该插入的位置 bisect(): 同值元素默认插在右侧 bisect_left(): 同值元素默认插在左侧 “”” print(bisect.bisect(int_list, 3)) print(bisect.bisect_left(int_list, 3))

result:

3

2

  1. <a name="58e7491a"></a>
  2. # 列表一定是最好的吗
  3. <a name="e4568847"></a>
  4. ## 引言
  5. - `Python`中的`array`,只可以存储指定类型的元素
  6. - 相当与是`C`语言中的数组,操作效率比列表更高
  7. - 官方文档:[https://docs.python.org/3.7/library/array.html?highlight=array](https://docs.python.org/3.7/library/array.html?highlight=array)
  8. <a name="568d18bd"></a>
  9. ## 使用案例
  10. ```python
  11. import array
  12. """
  13. 使用前要引入array模块,要申明存储的对象类型
  14. """
  15. my_array = array.array("i")
  16. my_array.append(666)
  17. my_array.append(4)
  18. my_array.extend([1, 2, 3])
  19. my_list = my_array.tolist()
  20. # 大部分基本的操作函数和序列差不多,这里就不做过多介绍
  21. print(my_array)
  22. # result:
  23. # array('i', [666, 4, 1, 2, 3])
  24. # [666, 4, 1, 2, 3]

列表推导、生成器表达式、字典推导

引言

  • 推导表达式是一种更加简洁高效,生成相关对象的一种方式

    使用案例

    ```python “”” 列表推导 生成1-20中的奇数 “””

    普通方式

    odd_list = [] for i in range(1, 21): if i%2 == 1:
    1. odd_list.append(i)

列表推导

odd_list = [i for i in range(1, 21) if i%2 == 1] “”” 生成器表达式 后面部分会详细介绍其原理 “”” odd_list = (i for i in range(1, 21) if i%2 == 1) print(type(odd_list))

result:

for item in odd_list: print(item, end=’ ‘)

result:

1 3 5 7 9 11 13 15 17 19

“”” 字典推导式 “”” my_dict = {“a”:1, “b”:2, “c”:3} reversed_dict = {val:k for k, val in my_dict.items()} print(reversed_dict)

result:

{1: ‘a’, 2: ‘b’, 3: ‘c’}

```