序列类型的分类
按照序列存储数据的类型
容器序列:list, tuple, deque
扁平序列:str, bytes, array.array, bytearray
按照序列是否可变
可变序列:list, deque, array.array, bytearray
不可变序列:str, tuple, bytes
序列的abc继承关系
引言
collections.abc模块中有很多内置的抽象基类
序列主要和两个抽象基类有关:Sequence 和 MutableSequence
细节
Sequence
"""
Sequence 抽象基类
它继承了Sized Iterable Contain 这三个抽象基类
"""
class Sequence(Sized, Iterable, Container):
"""All the operations on a read-only sequence.
Concrete subclasses must override new or init,
getitem, and len.
"""
slots = ()
@abstractmethod
def getitem(self, index):
raise IndexError
def iter(self):
i = 0
try:
while True:
v = self[i]
yield v
i += 1
except IndexError:
return
def contains(self, value):
for v in self:
if v == value:
return True
return False
def reversed(self): # 该魔法函数使得序列类型可以反转
for i in reversed(range(len(self))):
yield self[i]
class Sized(metaclass=ABCMeta):
slots = ()
@abstractmethod
def len(self): # 这个魔法函数使得序列类型可以使用 len()获得长度
return 0
@classmethod
def subclasshook(cls, C):
if cls is Sized:
if any("len" in B.dict for B in C.mro):
return True
return NotImplemented
class Iterable(metaclass=ABCMeta):
slots = ()
@abstractmethod
def iter(self): # 这个魔法函数使得序列类型可以进行 for 循环
while False:
yield None
@classmethod
def subclasshook(cls, C):
if cls is Iterable:
if any("iter" in B.dict for B in C.mro):
return True
return NotImplemented
class Container(metaclass=ABCMeta):
slots = ()
@abstractmethod
def contains(self, x): # 这个魔法函数使得序列类型可以使用 in 操作符
return False
@classmethod
def subclasshook(cls, C):
if cls is Container:
if any("contains" in B.dict for B in C.mro):
return True
return NotImplemented
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().@abstractmethod
br/>"""
@abstractmethod
def setitem(self, index, value):
raise IndexError
@abstractmethod
def delitem(self, index):
raise IndexError
def iadd(self, values): # 使得序列可以使用 += 运算符号进行计算
self.extend(values)
return self
......只列出几个有代表意义的魔法函数......
这些魔法函数使得序列类型可以改变
序列的+、+=和extend
引言
对于可变序列,我们有三种方式来扩充原始序列
三种方式有原理和效率上的不同,我们要根据实际情况选择合适的方式来使用
使用范例
"""
普通的加法会在内存中产生一个新的序列对象
时间 空间开销相对比较大
必须要是类型完全一致才能够使用这种合并方式
"""
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
"""
+= 可以理解为原地加,单纯地在原对象上进行修改,效率比 + 要高很多
这个功能背后有一个魔法函数,iadd(self, values)
上一小节中有这个函数具体的实现,我们可以看到,它是调用了 extend()方法
所以本质上,它和调用 extend()函数产生的效果是一模一样的
"""
a, b, c = [1, 2], [3, 4], (5, 6)
a += b
print(a)
result:
[1, 2, 3, 4]
a += c
print(a)
result:
[1, 2, 3, 4, 5, 6]
在鸭子类型那一小节,我们分析了extend()函数的参数要求,只要是可迭代类型都能够作为参数扩充列表。
+=操作本质上就是调用 extend() 函数,所以能将列表和元组进行合并也就不奇怪了,因为他们本质上都是可迭代的类型。
实现可切片的对象
引言
切片介绍
使用模式:[start:end:step]
"""
start: 切片开始的位置,默认是 0
end: 切片截止(不包括)的位置,默认是列表长度
step: 切片的步长,默认是 1
start和end为默认值时可以省略不写,step为默认值时可以连同最后的冒号一起省略
step为负数时,表示反向切片,此时需满足: start > end
"""
切片举例
li = [8, 4, 3, 2, 1, 7]
li[::] # 返回原序列
li[::-1] # 返回原序列的逆序列表
li[::2] # 隔一个取一个,所有偶数位置
li[1::2] # 隔一个取一个,所有奇数位置
li[3:6] # 取[3, 6)区间内的所有元素,左包含,右不包含
li[0:666] # 结束位置大于列表长度,从尾部截断,返回原列表
li[666:] # 开始位置大于列表长度,直接返回空列表
li = [6, 6]
li[:0] = [1, 2] # 列表头部插入元素 [1, 2, 6, 6]
li[1:1] = [1, 1] # 列表的某一位置插入元素 [6, 1, 1, 6]
li[:1] = [1, 1] # 替换列表元素 [1, 1, 6]
li[1:] = [1, 1] # 替换列表元素 [6, 1, 1]
使用案例
class Group:
def init(self, group_name, staffs):
self.group_name = group_name
self.staffs = staffs
def reversed(self): # reversed(group)
self.staffs.reverse()
"""
在使用切片和索引访问操作时,相关的参数会传递到 getitem(self, item) 中
我们通常希望,切片返回的对象和原对象是同一种类型
使用切片的话,传入的item是一个slice类型
使用索引访问,传入的item是一个int类型
"""
def getitem(self, item): # 对象可切片的关键
cls = type(self) # 获得该实例的类型
if isinstance(item, slice):
return cls(self.group_name, self.staffs[item]) # 委托给列表实现切片
elif isinstance(item, int):
return self.staffs[item]
def len(self):
return len(self.staffs)
def iter(self):
return iter(self.staffs) # 后面会详细讲解
def contains(self, item):
return item in self.staffs # 同样委托给列表的in操作来实现
def str(self):
return "name:{name}
staffs:{staffs}".format(name=self.group_name, staffs=self.staffs)
staffs = ["MetaTian0", "MetaTian1", "MetaTian2"]
group = Group("HIT", staffs)
print(group[1]) # 索引访问
print(group[0:2]) # 切片
reversed(group) # 反转
print(group)
print(len(group)) # 长度
print("MetaTian0" in group, "MetaTian6" in group) # in 操作符
resutl:
MetaTian1
name:HIT; staffs:['MetaTian0', 'MetaTian1']
name:HIT; staffs:['MetaTian2', 'MetaTian1', 'MetaTian0']
3
True False
bisect模块
引言
如果我们在一个有序序列中需要增加一个元素,但任然要维持序列的有序性,可以使用append()添加元素,再调用sort()来重新排序。
bisect模块用来维持已排序序列(升序)的顺序,效率更高。
两个关键函数insort()和bisect
使用案例
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
列表一定是最好的吗
引言
Python中的array,只可以存储指定类型的元素
相当与是C语言中的数组,操作效率比列表更高
官方文档:https://docs.python.org/3.7/library/array.html?highlight=array
使用案例
import array
"""
使用前要引入array模块,要申明存储的对象类型
"""
my_array = array.array("i")
my_array.append(666)
my_array.append(4)
my_array.extend([1, 2, 3])
my_list = my_array.tolist()
大部分基本的操作函数和序列差不多,这里就不做过多介绍
print(my_array)
result:
array('i', [666, 4, 1, 2, 3])
[666, 4, 1, 2, 3]
列表推导、生成器表达式、字典推导
引言
推导表达式是一种更加简洁高效,生成相关对象的一种方式
使用案例
"""
列表推导
生成1-20中的奇数
"""
普通方式
odd_list = []
for i in range(1, 21):
if i%2 == 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:
<class 'generator'>
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'}
喜欢python + qun:839383765 可以获取Python各类免费最新入门学习资料!
转载于:https://blog.51cto.com/14186420/2349821