自定义序列类
1 序列类型的分类
1.1 容器序列和扁平序列
容器序列和扁平序列是 Python 中两类基本的序列类型。容器序列存放的是元素的引用,即存放的是对象,而扁平序列存放的是元素的值,即存放的是值。
容器序列又分为 list、tuple 和 collections 模块中的一些其他类型,而扁平序列又分为 str、bytes、bytearray、memoryview 和 array.array,这些类型中存放的元素都是字符、字节或数值等基本类型,而不是对象。
1.1.1 容器序列
容器序列是存放不同类型数据的引用,可以存放不同类型的数据,包括基本数据类型和自定义类的实例等。
Python 容器序列包括:list、tuple 和 collections 模块中的一些其他类型,如 deque、Counter、OrderedDict、defaultdict 等。
1.1.2 扁平序列
扁平序列是存放相同类型数据的值,其中的元素类型通常是数值、字符或字节等基本类型,而不是对象。
Python 扁平序列包括:str、bytes、bytearray、memoryview 和 array.array 等。
1.2 可变序列和不可变序列
序列类型又可以分为可变序列和不可变序列两类。
可变序列可以通过索引修改序列中的元素,而不可变序列则无法修改其元素的值。
Python 中的可变序列包括 list、bytearray、array.array、collections.deque 等类型,而不可变序列包括 str、tuple、bytes 和 range 等类型。
2 abc的继承关系
所有的序列类型都是 collections.abc.Sequence 的子类,该类是 Python 标准库中的一个抽象基类。此外,可变序列还是 MutableSequence 的子类,而不可变序列则是 Reversible、Collection、Sized 和 Iterable 的子类。其中 Sized 是一个抽象基类,Reversible 和 Collection 是 Sized 的子类,而 Iterable 是 Collection 的子类。Sequence是Reversible和Collection的子类,可变序列有__seritem__
,__delitem__
,这些东西构成了python中的序列类型的协议。
collections.abc 是 Python 标准库中的一个模块,它定义了一些抽象基类,用于表示 Python 中的容器类型。其中,collections.abc.Sequence 是所有序列类型的抽象基类,MutableSequence 是所有可变序列类型的抽象基类,而 Reversible、Collection、Sized 和 Iterable 都是 Sized 的子类。
- Sized: 表示对象的大小可以被计算,即对象具有 len() 方法。它是 Reversible、Collection 和 Iterable 的父类。
- Reversible: 表示对象可以反向迭代,即对象具有 reversed() 方法。它是 Collection 的子类。
- Collection: 表示对象是一种集合类型,即对象具有 contains() 方法和 iter() 方法。它是 Sized 和 Iterable 的子类。
- Iterable: 表示对象是可迭代的,即对象具有 iter() 方法。它是 Sized 的子类。
除了上述抽象基类以外,collections.abc 还定义了其他一些有用的抽象基类,如 Mapping、MutableMapping、Set 和 MutableSet 等,用于表示 dict、set 等容器类型。
在 Python 中,抽象基类主要用于类型检查和代码设计。通过继承抽象基类,我们可以保证子类具有某些特定的行为和属性,从而提高程序的可读性和可维护性。
3 用python实现一个切片类型
3.1 切片介绍
Python 切片操作的语法为 sequence[start: end: step]
,其中的 sequence
可以是任意序列类型,start
、end
和 step
都是整数。
下面是一些 Python 切片操作的例子:
sequence[:]
:返回整个序列。sequence[start:]
:返回从start
开始到序列末尾的所有元素。sequence[:end]
:返回从序列开头到end
之前的所有元素。sequence[start:end]
:返回从start
开始到end
之前的所有元素。sequence[start:end:step]
:返回从start
开始到end
之间,间隔为step
的所有元素。sequence[::-1]
:返回逆序序列。sequence[::2]
:返回序列中所有的偶数位置的元素。sequence[1::2]
:返回序列中所有的奇数位置的元素。
注意,切片操作不会改变原序列,而是返回一个新的序列。如果需要修改原序列,需要使用赋值操作。
3.2 切片类实现
from collections.abc import Sequence
class MySliceable(Sequence):
def __init__(self, sequence):
self.sequence = sequence
# 切片关键 getitem
def __getitem__(self, index):
if isinstance(index, slice):
return MySliceable(self.sequence[index])
return self.sequence[index]
def __len__(self):
return len(self.sequence)
上面的代码实现了一个自定义的可切片类 MySliceable,它继承自 collections.abc.Sequence 抽象基类,并实现了其中的 __getitem__()
和 __len__()
方法。
__getitem__()
方法用于获取序列中指定位置的元素,它可以接受一个整数或一个切片对象作为参数。如果传入的是一个整数,则返回序列中对应位置的元素;如果传入的是一个切片对象,则返回一个新的 MySliceable 序列对象,其中包含了原序列中指定范围的元素。
__len__()
方法返回序列的长度,它是实现序列类型必须实现的方法之一。
通过继承抽象基类 collections.abc.Sequence,我们可以保证 MySliceable 类具有序列类型的一些基本行为和属性,从而可以使用 Python 中的一些序列相关函数和方法,如 len()、reversed()、list() 等。
下面是使用 MySliceable 类的例子:
sequence = MySliceable([1, 2, 3, 4, 5])
print(sequence[1]) # 输出 2
print(sequence[1:4]) # 输出 [2, 3, 4]
print(sequence[::-1]) # 输出 [5, 4, 3, 2, 1]
print(len(sequence)) # 输出 5
print(list(sequence)) # 输出 [1, 2, 3, 4, 5]
print(reversed(sequence)) # 输出 <list_reverseiterator object at 0x7f9c0b7f9ca0>
4 bisect模块
bisect 模块是 Python 标准库中的一个模块,用于处理排序后的序列。如果以后需要维持一个排序好的序列的话,可以直接使用bisect。
其中,bisect 函数用于将元素插入到已排序序列中的正确位置,insort 函数用于将元素插入到已排序序列中,并保持序列有序。
下面是 bisect 模块的一些常用函数和用法:
bisect_left 和 bisect_right 函数
bisect_left(a, x, lo=0, hi=len(a))
:将元素 x 插入到已排序序列 a 中,并返回插入位置(如果有多个相同元素,则插入到最左侧位置)。bisect_right(a, x, lo=0, hi=len(a))
:将元素 x 插入到已排序序列 a 中,并返回插入位置(如果有多个相同元素,则插入到最右侧位置)。
insort_left 和 insort_right 函数
insort_left(a, x, lo=0, hi=len(a))
:将元素 x 插入到已排序序列 a 中,并保持序列有序(如果有多个相同元素,则插入到最左侧位置)。insort_right(a, x, lo=0, hi=len(a))
:将元素 x 插入到已排序序列 a 中,并保持序列有序(如果有多个相同元素,则插入到最右侧位置)。
示例
下面是 bisect 模块的一些示例:
import bisect
a = [1, 2, 2, 2, 3, 4, 7]
print(bisect.bisect_left(a, 2)) # 输出 1
print(bisect.bisect_right(a, 2)) # 输出 4
print(bisect .insort_left(a, 2)) # 输出 None
print(a) # 输出 [1, 2, 2, 2, 2, 3, 4, 7]
print(bisect.insort_right(a, 2)) # 输出 None
print(a) # 输出 [1, 2, 2, 2, 2, 2, 3, 4, 7]
上面的代码演示了如何使用 bisect 模块的函数来处理排序后的序列。其中,bisect_left 和 bisect_right 函数用于查找元素的插入位置,insort_left 和 insort_right 函数用于插入元素并保持序列有序。
5 列表生成式,生成器表达式,字典推导式
5.1 列表生成式
列表生成式:odd = [i for i in range(21) if i % 2 == 1]
当然,列表生成式也可以实现很复杂的逻辑,下面是一个稍微复杂一些的例子:
words = ['hello', 'world', 'python', 'is', 'awesome']
# 将 words 列表中所有长度大于 4 的单词转换为大写形式,并保存到一个新的列表中
new_words = [word.upper() for word in words if len(word) > 4]
print(new_words) # 输出 ['WORLD', 'PYTHON', 'AWESOME']
上面的代码使用列表生成式将 words 列表中所有长度大于 4 的单词转换为大写形式,并保存到一个新的列表中。其中,word.upper()
表示将单词转换为大写形式,for word in words
表示对 words 列表中的每个单词进行遍历,if len(word) > 4
表示只选择长度大于 4 的单词。
另外,列表生成式中还可以包含多个循环和条件语句,例如:
# 将两个列表中的元素组合起来,并计算它们的和,保存到一个新的列表中
a = [1, 2, 3]
b = [4, 5, 6]
new_list = [x + y for x in a for y in b if x % 2 == 0 and y % 2 == 1]
print(new_list) # 输出 [6, 8]
上面的代码使用列表生成式将两个列表中的元素组合起来,并计算它们的和,只选择两个都是偶数或者两个都是奇数的元素。
5.2 生成器表达式
生成器表达式和列表生成式非常类似,但是它并不会一次性生成所有的元素,而是在需要的时候逐个生成,从而节省内存空间。生成器表达式的语法为 (expression for item in iterable)
,其中的 expression
是一个表达式,item
和 iterable
分别表示迭代变量和可迭代对象。
下面是一个使用生成器表达式的例子:
# 生成一个包含 1-100 之间的所有偶数的生成器
generator = (i for i in range(1, 101) if i % 2 == 0)
print(generator) # 输出 <generator object <genexpr> at 0x7f9c0b7f9ca0>
print(list(generator)) # 输出 [2, 4, 6, ..., 98, 100]
上面的代码使用生成器表达式生成了一个包含 1-100 之间的所有偶数的生成器,然后通过 list() 函数将生成器转换为列表。
注意,生成器表达式和列表生成式的语法非常相似,但是它们的执行方式不同。列表生成式会一次性生成所有的元素,因此占用的内存空间较大,而生成器表达式则是逐个生成元素,因此占用的内存空间较小。如果需要处理大量的数据,建议使用生成器表达式来节省内存空间。
5.3 字典推导式
字典推导式是一种快速创建字典的方式,其语法为 {key: value for item in iterable if condition}
,其中的 key
、value
、item
、iterable
和 condition
分别表示字典的键、值、迭代变量、可迭代对象和筛选条件。
下面是一个使用字典推导式的例子:
# 将一个列表中的元
素作为键,它们的平方作为值,保存到一个字典中
a = [1, 2, 3, 4, 5]
new_dict = {x: x ** 2 for x in a}
print(new_dict) # 输出 {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
上面的代码使用字典推导式将一个列表中的元素作为键,它们的平方作为值,保存到一个新的字典中。其中,x: x ** 2
表示将 x
作为键,x ** 2
作为值,for x in a
表示对列表中的每个元素进行遍历。
另外,字典推导式中还可以包含多个循环和条件语句,例如:
# 将两个列表中的元素组合起来,并计算它们的和,保存到一个字典中
a = [1, 2, 3]
b = ['a', 'b', 'c']
new_dict = {(x, y): x + y for x in a for y in b if x % 2 == 0 and y != 'a'}
print(new_dict) # 输出 {(2, 'b'): 4, (2, 'c'): 5}
上面的代码使用字典推导式将两个列表中的元素组合起来,并计算它们的和,只选择其中满足条件的元素。其中,(x, y): x + y
表示将 (x, y)
作为键,x + y
作为值,for x in a for y in b
表示对两个列表中的元素进行遍历,if x % 2 == 0 and y != 'a'
表示只选择满足条件的元素。
总之,字典推导式是一种快速创建字典的方式,可以大大提高编程效率。