Python进阶_序列构成的数组
[TOC]
数据结构
序列构成的数组
容器序列:储存对象的引用,可以存放多种类型的数据。
扁平序列:储存值,只能存放一种类型的数据。
可变序列:list、bytearray、memoryview、…
不可变序列:tuple 、str、bytes
列表推导式
list comprehension (listcomps)
1 | [ord(s) for s in words if ord(s) > 20] |
在
[]
、{}
、()
中,换行会被忽略。
也可以用 map
filter
完成:
1 | list(filter(lambda x:x > 20, map(ord, words))) |
哪一个效率更高?
通过以下程序可以粗略估计,列表推导式效率更高。
1 | import timeit |
输出:
1 | listcomp : 0.020 0.020 0.021 0.020 0.020 |
生成器表达式
generator comprehension (gencomps)
生成器 generator,逐个产出元素,能够节省内存。方便初始化元组、数组等序列。
圆括号
()
,作为参数时省略
1 | (ord(s) for s in words) |
元组拆包
元组重要的特点是“不可变”。
但是元组不仅储存了元素的信息,还储存了元素的位置信息,适合记录数据。
元组拆包 tuple unpacking:
- 平行赋值
1 | lax_coordinates = (33.9425, -118.408056) |
*
拆包可迭代对象
1 | t = (20, 8) |
*
获取剩余元素
1 | a, b, *rest = (1, 2, 3, 4) |
%
匹配元组元素
1 | traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')] |
- 嵌套元组拆包
1 | a, b, (c, d) = (1, 2, (3, 4)) |
关于占位符
_
:很多时候是一个很好的占位符,但是在国际化软件里,_
也是gettext.gettext
的常用别名。
具名元组
collections.namedtuple
:构建带字段名的元组和一个有名字的类。
用元组作为数据的记录,还少了字段的命名。因此使用具名元组。
1 | card = collections.namedtuple('Card', ['rank', 'suit']) |
具名元组还具有一些专有属性:
1 | from collections import namedtuple |
_fields
:字段名_make(iter)
:接受一个可迭代对象作为参数,构造具名元组_asdict()
:返回该实例的 collections.OrderedDict 形式。
序列切片
为什么切片会忽略最后一个元素?
1 | a = [1,2,3,4] |
- 方便知道切片中有多少元素
- 快速计算切片长度(末尾 - 起始)
- 用一个下标分为两部分
[:a] [a:]
切片对象 slice object
从 a
到 b
,以 c
为步长进行切片。
1 | A[a:b:c] |
1 | a:b:c => slice(a, b, c) |
多维切片
对于矩阵等二维数组,可以使用多维切片:
1 | [m:n, k:l] |
实质上是 __getitem__((m,n), (k,l))
省略号
1 | ... |
1 | a[i, ...] => a[i, :, :, :] |
切片实质上是对于原来序列中一部分元素的引用,修改切片会修改原序列。
序列的+和*
初始化一个由列表组成的列表?
1 | a = [[]] * 3 |
[[]] * n
实质上创建了 n 个指向同一个列表的引用。
该如何修改?
1 | a = [[] for i in range(3)] |
每次迭代都产生了新的列表实例。
序列的增量赋值
1 | a += b |
实质上是 __iadd__
和 __imul__
方法在起作用,如果第一个操作对象不具有这两个方法,那么就会调用 __add__
和 __mul__
。
Attention_1
__iadd__
和 __imul__
不改变变量的地址,而调用 __add__
和 __mul__
实质上执行了:
1 | a = a + b |
有一个赋值语句,将新的值赋给了新的对象,改变了 a 的地址。
Attention_2
1 | 1, 2, [3, 4]) a = ( |
会发生什么?
1 | Traceback (most recent call last): |
结果是既抛出了错误,又改变了不可变的元组!
查看字节码如下:
1 | "a[2] += [5, 6]") dis.dis( |
STORE_SUBSCR: a[2] = TOS
赋值操作抛出错误。
list.sort
与 sroted
区别:list.sort
为原址排序,不产生新的列表对象,返回 None
;而 sorted
返回新的排好序的列表。
连贯接口 fluent interface
参数:
reverse
:默认为False
,若为True
则降序输出key
:只有一个参数的函数,用于对序列中的每一个元素产生一个对比关键字。使用str.lower
实现忽略大小写的排序,使用len
实现根据字符串长度进行的排序……
算法使用的是 Timsort,会根据原始数据的顺序特点交替使用插入排序和归并排序。作者为 Tim Peters (Timbot),也是 Python之禅 的作者(
import this
)
bisect
操作已排好序的序列
bisect:二分,对半。
bisect
、insort
:利用二分查找算法在有序序列中查找或插入元素。
1 | # 在 haystack(干草垛)种搜索 needle(针)的位置 |
结果满足:把 needle 插入该位置后,haystack 还能够保持升序,
1 | import bisect |
输出:
1 | PS > python37 .\bisect_test.py |
1 | PS n> python37 .\bisect_test.py left |
还可以通过两个元素互相对应的有序列表,将一个列表中的值排序到对应的区间内 bisect_left
,再获取另一个列表中的对应值。
1 | def trans_grade(score, breakpoints=[60,70,80,90], grades="FDCBA"): |
bisect
将返回分数对应的第 i
个分数段,通过分数段序号取对应等级。
bisect = bisect_right
:所有的haystack[:i]
都小于等于 needle
bisect_left
:所有的haystack[:i]
都小于 needle,也就是遇上了相等元素,新元素放在前面
Attention!
str.format
,参数作为一个元组传入,format string 中的{m:nX}
表示:取参数元组中的第m
个,字段宽度为n
,按照数据类型X
右对齐输出。
.join()
接受一个序列类型,或者一个序列生成器。
sys.argv
获取命令行参数。
列表不是首选时
列表背后存储的是 int
、float
等对象,而数组 array
存储数字的字节表述。
如果需要频繁对序列进行先进先出操作,双端队列 deque
速度更快。
array
模块
1 | # 根据 类型码 和 初始值 创建存储为字节码的数组 |
需要注意的是,array.tofile
和 array.fromfile
用起来简单且快速,比从文本读入快多了。因为后者会调用 float
方法将每一行文字转换为浮点数。并且写入二进制文件 *.bin
时,占用的空间更少。不过, python3.4 之后,数组不支持就地排序,需要用 sorted
。
memoryview
内存视图。在不复制内容的情况下操作同一个数组的不同切片。处理大型数据集合时很重要。
1 | # 创建 短整型有符号整数 数组 'h' |
‘h’ 占 2 个字节,’B’ 占 1 个字节,因此使用
memvoryview.cast('B')
时,每个短整型有符号数字被划分为高位 + 低位。例如:-2
的字节码划分为高位和低位就是 254、255。
NumPy
和 SciPy
另有笔记进行学习。
双端队列
普通列表通过 append
和 pop
模拟栈。
collections.deque
支持更快速、线程安全的队列类型。maxlen
参数指明队列容纳的元素个数。
rotate(n)
:将队列的右侧(n>0)n个元素移动到左侧,反之移动到右侧。1
2
3
4
5
6
710), maxlen = 10) dq = collections.deque(range(
dq
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)
3) dq.rotate(
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6], maxlen=10)
-4) dq.rotate(
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], maxlen=10)appendleft
:左侧添加元素,若队列已满则删除最右侧元素。1
2-1) dq.appendleft(
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9], maxlen=10)extend
:将序列中的元素依次添加到右侧,挤出左侧元素。1
211,22,33]) dq.extend([
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33], maxlen=10)extendleft
:将序列中的元素依次添加到左侧,挤出右侧元素。1
2>>> dq.extendleft([44,55,66,77])
deque([77, 66, 55, 44, 3, 4, 5, 6, 7, 8], maxlen=10)
append 和 popleft 都是原子操作,也就说是 deque 可以在多线程程序中安全地当作先进先出的栈使用,而使用者不需要担心资源锁的问题。
其他队列
queue
:同步类Queue、LifoQueue 和 PriorityQueue
multiproessing
:进程通信用、JoinableQueue
用于任务管理
asyncio
:包括前两者提供的 Queue,用于异步编程
heapq
:堆队列、优先队列