数据结构序列数组元组和记录
切片+与*排序 数组内存视图memoryview队列
双向队列collections.deque其他队列
序列数组内置类型
容器序列:list、tuple、collections.deque这些序列可以存放不同类型的数据。
扁平序列:str、bytes、bytearray、memoryview和array.array只能容纳一种类型。里面是一段连续的内存空间。
可变序列(MutableSequence):list、bytearray、array.array、collections.deque、memoryview
不可变序列(Sequence):tuple、str、bytes
不可变序列有__getitem__ __contains__ __iter__ __reversed__ index count 等方法
可变序列有__setitem__ delitem__ insert append reverse extend pop remove __iadd__等方法
列表推导
python会忽略括号内换行([]、{}、()),因此列表推导的时候可以省略续行符列表推导可以代替map/filter的组合。但尽量在合适且易读的情况下使用。
举例:
x = [1,-2,3,-9,0,1] positive = [ abs(i) for i in x]
positive = [1,2,3,9,0,1]
笛卡尔积:
计算颜色与尺寸的组合,也就是两个列表相乘构成元组列表。
colors = ["black","white"] sizes = ['S','M','L'] results = [ (color, size) for color in colors for size in size ]
最后results中的排列顺序则先排颜色(先是black对应三个尺寸,在是white对应三个尺寸),如果用for循环实现,则colors是外层的循环,size是内层的。
生成器表达式
与列表推导类似,但是是圆括号构成,遵守迭代器协议,可以逐个地产生元素。
举例:
# 元组tuple
test="exαmp1e"
print(tuple( ord(x) for x in test))
# 数组array
import array
print(array.array('I', (ord(x) for x in test)))
输出:
(101, 120, 945, 109, 112, 49, 101)
array('I', [101, 120, 945, 109, 112, 49, 101])
元组和记录
元组是对数据的记录,每个元素都存放了记录中一个字段的数据和这个字段的位置。
拆包(unpacking)
对元组进行拆包(unpacking):
# 赋值 position = (0.2, 0.9, 1,1) x,y,z = position # 交换 x, z = z, y # 把可迭代对象拆开作为函数的参数 test = (x*20, y*100) newX, newY = divmod(*test) print(newX, newY) # 为了保持参数数目一致,用下划代替 x, y, _ = position # 用*处理多余的参数 x, *pos = position print(x, pos) *pos,z = position print(pos, z)
元组拆包可以应用到任何可迭代对象上,唯一的硬性要求是被迭代时,元素数量要保持一致,除非用*来处理多余的元素。
嵌套元组拆包
test = [ (‘beijing’, (1,2)) (‘chengdu’, (3,4))] for city, (x,y) in test: print(city) print(x/y) 吧
具名元组
利用工厂函数 collections.namedtuple构建带字段名的元组和一个有名字的类(便于调试)。
具名元组可以快速构造类,但和字典不同(字典可以修改,具名元组不可以)
from collections import namedtuple
Student = namedtuple('Student', 'name age gender')
xiaoming = Student('xiaoming', 16, 'boy')
print(xiaoming)
Student(name='xiaoming', age=16, gender='boy')
通过具名元组构造的类的对象,消耗的内存比一般的对象实例更小,python解释器不会用__dict__来存储实例的属性。如上例所示,namedtuple传入的两个参数分别是类名,类的各个属性名(用空格键隔开),两个参数都是字符串。namedtuple(className,properties)属性(字段)的赋值,需要在调用构造函数时传参。并且可以通过属性名(字段名)和位置来获取信息:例如:print(xiaoming[1], xiaoming.age) 这两个是等价的。
具名元组的属性和方法:
_fields:属性,包括这个类的所有字段名称的元组,xiaoming._fields输出('name', 'age', 'gender')_make(*item):通过接受一个可迭代对象(例如一个元组)来生成这个类的一个实例_asdict():把具名元组以collections.OrderedDict的形式返回,我们可以利用它来把元组里面的信息友好地呈现出来。xiaoming._asdict()输出{'name': 'xiaoming', 'age': 16, 'gender': 'boy'} 切片
列表、元组、字符串都支持切片s[start: end: interval]
start是起始,end是结束的索引(但不会被包含在内),interval指的是对s在start和end之间以interval为间隔取值。start和end可以不写。
interval可以取负值,当interval为负值时,可以理解为还是从start到end以|interval|为间隔取值,但结果经过了一次逆序。
切片可以赋值,但赋值语句右边必须也为可迭代对象。
# 取切片 s='bicycle' # 输出结果 s[::3] = 'bye' s[::-1] = 'elcycib'+与*
这一节平时用的比较多就不赘述。
但关于+=的谜题很有意思:
即:
>>> t = (1,2,[30,40]) >>> t[2]+= [50,60] Traceback (most recent call last): File "", line 1, in TypeError: 'tuple' object does not support item assignment >>> t (1, 2, [30, 40, 50, 60]) >>> t = (1,2,[30,40]) >>> t[2].extend([50,60]) >>> t (1, 2, [30, 40, 50, 60])
t这个元组中,t[2]是列表,是一个可变对象,而s是不可变的元组,因此赋值过程中会报错。
尽量不要把可变对象放到元组内。增量赋值+= 不是原子操作可以用dis.dis('s[a]+=b')查看这一操作的字节码 排序
list.sort(): 对list直接排序,返回Nonesorted(list): 返回排序好的新list,而不改变原list
第一个参数是需要排序的list,第二个参数是reverse(默认为False),第三个参数是key
min()和max()也有key这个参数,用来定义排序规则的函数关键字。key的默认值为恒等函数(identity function),也就是元素的值。若list中每个值是字符串,key=str.low意思是忽略大小写,key=len的意思是按照字符串的长度来排,推测sort函数在对比时会调用key这个函数。
这两个函数背后的排序算法为Timsort,会根据原始数据特点交替使用插入排序和归并排序
bisect
利用二分查找算法来在有序的序列中查找或者插入新的元素。
bisect.bisect:搜索,bisect.bisect(list, search_list), list是原序列,search_list是要搜索(若不存在则插入)的数据,返回搜索到的数据的位置,如果这个位置有和搜索目标相等的数据,则返回后一个位置(插在后面);bisect.bisect_left(list, insert_list) 返回的位置则相反,如果有相等数据,就返回该位置,插在这个数据前面。
反过来也可以利用这个搜索功能建立索引,实现一一对应的功能:
def grade(score, breakpoints=[60,70,80,90],grades="FDCBA"): i = bisect.bisect(breakpoints, score) return grades[i] print( grade(score) for score in [33, 99, 77, 70, 89]) # 输出 ['F', 'A', 'C', 'C', 'B']
bisect.insort: 插入新元素且保持序列升序,和bisect一样有可选参数来控制查找的范围:lo,默认为0,hi默认为序列的长度len。
数组列表list有时候不是首选,存放大量浮点数时数组更高效,需要频繁先进先出的话双端队列deque更高效。
只包含数字时,array.array比list更高效,因为里面存储的是数字的机器翻译(字节表达)。
数组支持.pop .insert .extend等方法,以及从文件读写的.frombytes .tofile方法。
举例:
from array import array
floats = array('d', (0 fro i in range(20)))
pickle.dump和 array.tofile几乎一样快,而且pickle可以处理所有内置数字类型(复数、嵌套集合、简单自定义类等)
array与list对比:
array有但list没有的属性和方法:s.byteswap s.frombytes(b) s.fromfile(f,n) s.fromlist(l) s.tobytes() s.tofile() s.tolist() s.typecode s.__deepcopy__() s.__copy__()
s.byteswap是翻转数组内每个元素的字节序列,转换字节序。
s.typecode是返回只有一个字符的字符串,代表数组元素在C语言中的类型。
反之只有list拥有的属性和方法:s.clear() s.copy() s.sort()
其他方法例如append、__contains__、extend、remove、 pop 、reverse都是通用的了。
能让用户在不复制内容的情况下操作同一个数组的不同切片
内存视图其实是泛化和去数学化的NumPy数组,任意数据结构都可以共享内存
我理解的是操作内存视图的时候,操作的是该地址空间上的数据。因为memoryview是一个连续的地址空间,所以切片操作是完全可行的。就像指针一样,避免了反复的数据复制。
后文中numpy和scipy因为用的比较多,也有别的文章和文档,就不再记录。
队列 双向队列collections.deque线程安全,快速从两端添加和删除元素的数据类型。
一般的列表拥有append、pop方法可以模拟栈的操作,双向队列多出了append_left、pop_left方法
list.pop(p) 默认p为最后一个数的目录,p可以传参,而deque.pop()不传参,只能弹出最后一个数。
其他队列| 名称 | 描述 |
|---|---|
| queue | 提供了同步(线程安全)类Queue、LifoQueue和PriorityQueue,不同线程可以利用这些数据类型交换信息。可选参数maxsize会限定队列的大小,但满员时不会丢掉旧的数据,而是锁住,直到某个线程为它腾出位置。 |
| multiprocessing | 这个包实现了自己的Queue,供进程间通信使用。还有multiprocessing.JoinableQueue |
| asyncio | python3.4新包,有Queue、LifoQueue、PriorityQueue、JoinableQueue,为异步编程提供便利。 |
| heapq | 没有队列类,提供了heappush和heappop方法,可以把可变序列当作堆队列或者优先队列来使用。 |



