# 第五章:自定义序列类
# 5.1 python中的序列分类
序列是 python 中重要的协议
按照元素类型是否相同
- 容器序列:list、tuple、deque
- 扁平序列:str、bytes、bytearray、array.array
按照元素是否可变
- 可变类型:list、deque、bytearray、array.array
- 不可变:str、tuple、bytes
# 元素类型任意
my_list = list()
my_list.append(100)
my_list.append(True)
# 指定元素类型
import array
my_array = array.array('i')
my_array.append(100)
# 初始化数组需要整型,附加字符串抛异常
my_array.append('abc')
# 5.2 序列的abc继承关系
python 中内置的 collections.abc 抽象基类,可以帮助我们理解数据类型实现细节
python 是基于协议的语言,结合鸭子类型和魔法函数,就可以达到实现某种类型
from collections.abc import *
- Iterable: iter
- Reversible: reversed
- Sized: len
- Container: contains
- Collection: Sized, Iterable, Container
- Sequence: getitem, Reversible, Collection
- MutableSequence: setitem, delitem, Sequence
不同魔法函数的组合,构建不同的类型
# 5.3 序列中+、+=和extend的区别
- 加号 + 会新生成对象,并且两边类型需要一致
- 加等 += 就地加,只需要可迭代就行
- append 附加单个元素,extend 扩展多个元素
# + 加新增
l1 = [1, 2]
l2 = l1 + [3, 4]
print("type(l1): %d, and l1: %s" % (id(l1), l1))
print("type(l2): %d, and l2: %s" % (id(l2), l2))
# += 就地加
l1 += ['3', '4']
l1 += ('5', '6')
l1 += range(2)
print("type(l1): %d, and l1: %s" % (id(l1), l1))
# + 两边类型需相同
# += 只需要可迭代的就行,__iadd__ 魔法函数实现
# 5.4 实现可切片的对象
# 列表切片操作
'''
模式 [start:end:step]
第一个数字 start 表示切片开始位置,默认 0
第二个数字 end 表示切片截止(但不包含)位置,默认列表长度
第三个数字 step 表示切片的步骤,默认为 1
当 start 为 0 时可以省略
当 end 为列表长度时可以省略
当 step 为 1 时可以省略,并且省略步长时可以同时省略最后一个冒号
当 step 为负数时,表示反向切片,这时 start 应该比 end 的值要大才行
'''
a_list = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
a_list[::] # 返回包含原列表中所有元素的新列表
a_list[::-1] # 返回包含原列表中所有元素的逆向新列表
a_list[::2] # 隔一个元素取一个,获取偶数位置的元素
a_list[1::2] # 隔一个元素取一个,获取奇数位置的元素
a_list[3:6] # 指定切片的开始和结束位置
a_list[0:100] # 切片结束位置大于列表长度是,从列表尾部截断
a_list[100:] # 切片开始位置大于列表长度时,返回空列表
a_list[len(a_list):0] = [9] # 在列表尾部增加元素
a_list[:0] = [1, 2] # 在列表头部增加元素
a_list[3:3] = [100] # 在列表中间位置插入元素
a_list[:2] = [100, 200] # 替换列表元素,等号两边长度相等
a_list[3:] = [4, 5, 6] # 替换列表元素,等号两边长度可以不相等
a_list[:3] = [] # 删除列表中前 3 个元素
# 自定义序列类
参考 collections.abc 序列类 Sequence 所需的魔法函数
import numbers
class Group:
def __init__(self, group_name, company_name, staff):
self.group_name = group_name
self.company_name = company_name
self.staffs = staff
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, staff=self.staffs[item])
elif isinstance(item, numbers.Integral):
return cls(group_name=self.group_name, company_name=self.company_name, staff=[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 = ['linda', 'alex', 'catherine', 'nash', 'curry']
group = Group(group_name='group', company_name='company', staff=staffs)
sub_group = group[0]
print('linda' in sub_group)
print(len(sub_group))
# 5.5 bisect维护已排序序列
- bisect 维护一个升序的序列
- 内部二分查找实现,效率高
import bisect
# 处理已排序 序列 升序
# 内部二分查找算法实现
l1 = list()
bisect.insort(l1, 10)
bisect.insort(l1, 3)
bisect.insort(l1, 2)
bisect.insort(l1, 6)
print(l1) # [2, 3, 6, 10]
# 5.6 什么时候我们不该用列表
- 比 list 更好的 python 内置数据结构
- array 数组 连续的内存空间,性能高
- deque 双向列表
# array 数组
array 与 list 一个重要区别,array 只能存储指定的数据类型数据
import array
list
my_array = array.array('i')
my_array.append(100)
my_array.append('abc')
- 某些应用场景,除了 list 我们还有其他更好的选择
# 5.7 列表推导式、生成器表达式和字典推导式
# 列表推导式
列表推导式,或列表生成式,通过一行代码生成列表
# 提取出 1-20 之间的奇数
odd_list = [i for i in range(21) if i % 2 == 1]
print(odd_list)
# 逻辑复杂的情况
def handle_item(item):
return item * item
odd_list = [handle_item(i) for i in range(21) if i % 2 == 1]
print(odd_list)
- 列表生成式性能高于列表操作
- 逻辑过于复杂时,列表生成式可读性降低
# 生成器表达式
列表推导 [] -> ()
my_gen = (i for i in range(21) if i % 2 == 1)
print(type(my_gen)) # <class 'generator'>
for i in my_gen:
print(i)
# 字典推导式
d1 = {'key1': 'value1', 'key2': 'value2'}
d2 = {v: k for (k, v) in d1.items()}
print(d2)
# 集合推导式
set1 = {v for v in d1.values()}
print(set1)
# 5.8 本章小结
- 序列类型的分类
- 序列的abc继承关系
- 序列的+、+=和extend的区别
- 实现可切片的对象
- bisect管理可排序序列
- list的其他选项
- 列表字典推导式