流畅的python
第1章 Python数据模型
如何使用特殊方法
len 和 getitem
python中所有集合类型都可以使用len()函数查看集合长度,使用len(obj)而不是obj.len(),也不是obj. len (),如果是自定义类的对象,python会自己调用类中实现的 len 方法
魔法方法/特殊方法/双下方法,使用len()的形式调用
字符串表示形式
repr 方法得到一个对象的字符串表示形式
1 | "{} {}".format("hello", "world") # 不设置指定位置,按默认顺序 |
字符串.join(‘分隔符’,字符串列表\元组)方法
使用指定的分隔符将字符串连接起来,如果分隔符指定为’’,则直接连接
1 | a = "123" |
变量的存在域
- python能够改变变量作用域的代码段是def、class、lamda.
第2章 序列构成的数组
list可以容纳不同类型的元素,但如果列表里的元素不能比较大小,就不能进行排序,但如果是字符和数字可以利用key参数p53
列表推导和生成器表达式
列表推导
python3中列表推导不会再有变量泄露问题,列表推导和生成器表达式都有局部作用域,表达式内部的变量和赋值只起局部作用,不影响表达式上下文中的同名变量。
1 | x = 'ABC' |
笛卡儿积
python会忽略代码里[],{},()中的换行
1 | tshirts = [(color,size) for color in colors |
生成器表达式
初始化元组、数组或者其他序列类型,使用生成器表达式
语法与列表推导相似,把方括号换成圆括号
列表推导也可以用来初始化元组、数组或其他序列类型(先构造列表,然后把列表传递到构造函数中),但使用生成器表达式更好,因为生成器表达式遵守了迭代器协议,可以逐个产出元素,而不是先建立一个完整的列表,再将列表传递到构造函数中,这样可以节省内存
1 | for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes): |
列表乘法
对Python的列表使用乘法,如果列表的元素是不可变对象(如数字、字符串)则是复制值,如果是可变对象(如列表、字典)则是复制引用,因此对于包含可变对象的列表切莫使用列表乘法,可使用列表生成式代替。
用一个列表list1乘一个数字n 会得到一个新的列表list2, 这个列表的元素是list1的元素重复n次, 例如
1 | list1 = [0] |
但如果列表元素为可变对象如列表,乘法的结果是复制引用,也就是多个重复元素是共用一个地址id的
1 | list1 = [[0, 0]] |
因此这种情况下,应该使用列表生成式
1 | 'a': 1} for _ in range(4)] l = [{ |
元组
- 不可变的列表
- 对数据的记录
对数据的记录
元组拆包
元组拆包可以应用在任何可迭代对象上,唯一硬性要求是元素数量要对应,除非使用*来忽略多余元素
1 | # 应用1 |
使用*处理剩余元素
1 | a, *rest, c, d = range(5) |
嵌套元组拆包
如(a, b, (c, d))的形式
具名元组
使用collections.namedtuple构建一个带字段名的元组和一个有名字的类,目的是方便调试
1 | from collection import namedtuple |
作为不可变的列表
列表和元组的常见属性和方法p27
切片
使用切片对象slice增加代码可读性
如果进行切片操作的时候,超出下标不会报错
如果切片操作的时候方向相互矛盾的话,不会报错,返回为空
在python中进行反向输出列表
多维切片和省略(…)
给切片赋值
list和numpy中的np.array的区别
np.array和list均支持切片操作,且切片范围超过实际大小时不会报错
np.array中arr_m[0, 0]与arr_m[0] [0]等价,而list中只能以list_m[0] [0]方式访问,而不能list_m[0, 0]
1 | import numpy as np |
对序列使用+和*(拼接)
不修改原有的操作对象,而是构建一个全新的序列
1 | print([' ']*4) |
序列的增量赋值(+=,*=)
a+=b
+=实际上调用了特殊方法 iadd,用于就地加法,与a.extend(b)效果一样,但如果类没有实现 _ _iadd _ _则调用 _ _add _ _,此时a+=b就和a=a+b一样,a+b产生一个新的对象,然后再把a指向a+b的地址.
可变序列如list都实现了_ _iadd _ _方法,也就是会就地改动,不可变序列不支持。
*=和+=类似
1 | # 可变序列的重复拼接 |
列表常用操作
1 | s=[] |
extend() 函数、append()函数、+ 与 += 功能比较:
- append()是向列表尾部追加一个新元素,列表只占一个索引位,在原有列表上增加。
- extend()向列表尾部追加一个列表,将列表中的每个元素都追加进来,在原有列表上增加。
- +与extend()在效果上具有相同的功能,但是实际上生成了一个新的列表来存放这两个列表的和,只能用在两个列表相加上。
- +=与extend()效果一样。
list.sort方法和内置函数sorted
list.sort就地排序,返回None,是为提醒本方法不会新建一个列表(会修改原数据)
sorted会新建一个列表作为返回值,可以接受任何形式的可迭代对象作为参数,包括不可变序列和生成器(不会修改原数据)
list.sort和sorted都有两个可选的关键字参数
- reverse:默认为False,升序,True,降序
- key:一个只有一个参数的函数,会被用在序列里的每个元素上,产生的结果将是排序算法依赖的对比关键字。如key = str.lower忽略大小写,key=len基于长度排序
p53,python中的key是一个单参数的函数,区别于其他语言中cmp函数是双参数的,好处是更快。
用bisect来管理已排序的序列
bisect模块的两个主要函数,bisect和insort,均利用二分查找在有序序列中查找或插入元素
1 | import bisect |
bisect函数是bisect_right的别名,返回跟他相等的元素之后的位置,相对的还有bisect_left,返回和插入元素相等的位置,也就是会被放在前面
用bisect.insort插入新元素,insort(seq, item)把变量item插入到序列seq中,且保持seq的升序
1 | import bisect |
当列表不是首选时(某些情况下可以替换列表的数据类型)
数组
内存视图 memoryview
NumPy和SciPy
队列
利用list的.append和.pop方法,是可以把list当作栈或队列来用的,比如利用.append和.pop(0),就可以模拟队列的先进先出,但是删除第一个元素的操作是非常耗时的,因为会移动列表里的所有元素
collection.deque类,也就是双向队列,可以快速从两端添加或删除元素
1 | from collection import deque |
对一个已经满了的队列做添加操作时,会删除掉另外一侧的元素
队列的其他操作p48
其他标准库对队列的实现
- queue
- multiprocessing
- asyncio
- heapq
第3章 字典和集合
可散列的数据类型
散列表是字典类型性能出众的根本原因
标准库里所有映射类型都是利用dict来实现的,因此有个共同的限制:只有可散列的数据类型才能作为这些映射里的键(只有键有要求,值可以不是)。不可变的数据类型如str、bytes、数值类型都是可散列的,当元组包含的所有元素都是可散列类型的情况下也是散列的。可变类型list不是可散列的。
字典的构造与推导
常见构造方法
字典推导dictcomp
从任何以键值对作为元素的可迭代对象中构建出字典
典型结构
1 | key:value for var in iterable |
例子
1 | # 从字典中提取特定键值对 |
映射类型的常见方法
p57
处理找不到的键
get()方法
dictname.get(key[,value])
key为指定键值
value是可选参数,若指定键值不存在时则返回value或者None
get()方法与dict[key]区别
get(key) 方法在 key(键)不在字典中时,可以返回默认值 None 或者设置的默认值。而dict[key] 在 key(键)不在字典中时,会触发 KeyError 异常
setdefault
查找不到时,插入新的键值对
my_dict.setdefault(key,[])作用:如果key不存在则创建key和值为[]的键值对放入my_dict
1 | my_dict.setdefault(key,[]).append(new_value) |
defaultdict
创建defaultdict对象时需要配置一个为找不到的键创建默认值的方法,提供一个可调用对象,如调用list()来创建一个新列表,作为值
1 | my_dict = collections.defaultdict(list) |
_ _missing _ _
字典的变种
OrderedDict
保持顺序
OrderedDict.popitem() 会移除字典里最先插入的元素(先进先出);同时这个方法还有一个可选的 last 参数,若为真,则会移除最后插入的元素(后进先出)。
collections.Counter
这个映射类型会给键准备一个整数计数器。每次更新一个键的时候都会增加这个计数器。所以这个类型可以用来给可散列表对象计数,或者是当成多重集来用。多重集合就是集合里的元素可以出现不止一次。Counter 实现了 + 和 - 运算符用来合并记录,还有像 most_common([n]) 这类很有用的方法。most_common([n]) 会按照次序返回映射里最 常见的 n 个键和它们的计数
集合 set/frozenset
集合的本质是许多唯一对象的聚集。因此,集合可以用于去重
对于给定的两个集合a,b
a | b返回合集,a & b返回交集,a - b返回差集
1 | found = len(set(set_a) & set(set_b)) |
常见操作
1 | lookup = set() |
数据结构
32位机器,一个整数占4字节,一个地址占4字节,最大内存为2^32-1约为4G
列表
列表中的元素是顺序存储的,在连续空间中存储,与数组区别在于,可以存储不同类型的元素,长度不固定
由于列表所存元素可能不是同一类型的,所以python在实现列表时,实际上存的是地址
长度固定的问题,python通常是开辟一片空间,不够了就会新开更大的空间,然后将原list拷贝过去
常用操作的时间复杂度
下标查找,append,不考虑拷贝,O(1)
插入insert和删除remove O(n),删了还需要移动元素
栈Stack
使用一般的列表结构即可实现栈
可以理解为只能在一端进行插入或者删除操作的列表,后进先出
队列Queue
在列表的一段进行插入(队尾rear),另一端进行删除(队首front),先进先出
没办法使用列表简单实现,需要通过环形队列实现的
环形队列:
- 当队尾指针front == Maxsize + 1时,再前进一个位置就自动到0.
- 队首指针前进1: front = (front + 1) % MaxSize
- 队尾指针前进1: rear = (rear + 1) % MaxSize队空条件: rear == front
- 队满条件:(rear + 1) % MaxSize == front,留了一个空间没有存数,用于区别队空和队满的情况
双向队列(两端都支持进队和出队)
python内置模块
1 | from collections import deque |
哈希表
字典和集合就是使用哈希表实现的
支持高效操作:
- insert(key, value):插入键值对(key,value)
- get(key)∶如果存在键为key的键值对则返回其value,否则返回空值
- delete(key):删除键为key的键值对