文章目录
第一部分 序幕
1.Python数据模型
-
collections.namedtuple
构建只有少数属性但没有方法的对象 -
random.choice
随机函数 -
特殊方法是为了被Python解释器调用,一般自己不需要调用,除了经常使用
__init__
方法,目的是在子类的__init__
方法中调用超类的构造器。 -
complex类可以表示二维向量,
complex(1,2)
表示 1 + 2 j 1+2j 1+2j -
repr 就是通过
__repr__
这个特殊方法来得到一个对象的字符串表示形式的。类似与java中的tostring()
第二部分 数据结构
2.序列构成的数组
- 列表推导的作用只有一个:生成列表。如果要生成其他类型的序列,需要生成器表达式
- 元组不仅仅是不可变的列表,还可以用于没有字段名的记录
- 用
*args
来获取不确定数量的参数,用*
运算符可以把一个可迭代对象拆开作为函数的参数 - 切片
seq[start:stop:step]
- 不要把可变对象放在元组里面
内置序列类型
- 容器序列:list、tuple 、collections.deque 这些序列能存放不同类型的数据
- 扁平序列:str、bytes、bytearray、memoryview 、array.array,这类序列只能容纳一种类型
- 可变序列:list、bytearray、array.array、collections.deque、memoryview
- 不可变序列:tuple、str 、bytes
list.sort()
就地排序返回None,sorted()
新建一个新排序的列表,不改变原列表。二者背后的排序算法是Timsort,是一种自适应算法,会根据原始数据顺序特点交替使用插入排序和归并排序,以达到最佳效率
bisect模块的bisect和insort函数都是利用二分查找算法在有序序列中查找或插入元素
bisect.bisect_left(list, insert_num, lo=0, hi=len(list))
:返回list中插入insert_num的索引,若已存在insert_num,返回最左边的bisect.bisect_right(list, insert_num, lo=0, hi=len(list))
:返回list中插入insert_num的索引,若已存在insert_num,返回最右边的bisect.bisect(list, insert_num, lo=0, hi=len(list))
:同bisect.bisect_right()bisect.insort_left(list, insert_num, lo=0, hi=len(list))
:返回插入元素后的列表bisect.insort_right(list, insert_num, lo=0, hi=len(list))
:返回插入元素后的列表bisect.insort(list, insert_num, lo=0, hi=len(list))
:返回插入元素后的列表
快速序列化数据类型:
array.tofile()
和array.fromfile()
- pickle
Pickle
在程序运行过程中,所有变量(所有数据类型如列表,字典,集合,类等)都是在内存中,但一旦程序结束,变量所占用的内存就被操作系统全部回收。变量从内存中变成可存储或传输的过程称之为序列化pickling。序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上,但是可读性差,人一般无法识别。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化unpickling。
import pickle
s = {1, 2, 3}
# pickle.dumps把obj对象序列化后以bytes对象返回,不写入文件
pickle_dumps = pickle.dumps(s)
print(pickle_dumps) # b'\x80\x03cbuiltins\nset\nq\x00]q\x01(K\x01K\x02K\x03e\x85q\x02Rq\x03.'
# pickle.loads从bytes对象中读取一个反序列化对象,并返回其重组后的obj
pickle_loads = pickle.loads(pickle_dumps)
print(pickle_loads) # {1, 2, 3}
# pickle.dump序列化对象,并将结果数据流写入到文件对象中
with open("file", "wb") as f:
pickle.dump(s, f)
# pickle.load反序列化对象,将文件中的数据解析为python对象
with open("file", "rb") as f:
pickle_load = pickle.load(f)
print(pickle_load) # {1, 2, 3}
- 内存视图:memoryview是一个内置类,可以在不复制内容的情况下操作同一个数组的不同切片
deque.rotate(n)
:队列的旋转,n>0时队列最右边n个元素移到左边,n<0时队列最左边n个元素移到右边,n可以大于maxlen
3.字典和集合
如果一个对象是可散列的,在这个对象的生命周期中它的散列值是不变的。当所有内部状态都是不可变时,这个对象才是可散列的。不可变类型(内部状态也不可变)都是可散列的。
创建字典的不同方式
a = dict(one = 1, two = 2)
b = {'one': 1, 'two': 2}
c = dict(zip(['one', 'two'], [1, 2]))
d = dict([('two', 2), ('one', 1)])
e = dict({'two': 2, 'one': 1})
print(a == b == c == d == e) # True
- 三种映射类型的方法列表:dit、clollections.defaultdict和collection.OrderedDict
- d[k]找不到正确的键时,用
d.get(k, default)
代替 - 初始化
dict.setdefault(key, [])
字典的变种:
- collections.OrderedDict
添加键时会保持顺序,键的迭代顺序总是一致的,其popitem方法默认删除并返回字典里的最后一个元素 - collections.Counter
most_common([n])
按次序返回映射里最常见的n个键及其计数 - collections.UserDict
import collections
ct = collections.Counter('adasdadsads')
print(ct) # Counter({'a': 4, 'd': 4, 's': 3})
ct.update('aaazzzz')
print(ct) # Counter({'a': 7, 'd': 4, 'z': 4, 's': 3})
print(ct.most_common(2)) # [('a', 7), ('d', 4)]
types模块中引入了一个封装类名叫MappingProxyType。如果给这个类一个映射,会返回一个只读的动态映射视图,如果对原映射作出改动,通过这个映射视图可以观察到但无法对原映射作出修改。
给定集合a和b
- 并集 a | b
- 交集 a & b
- 差集 a - b
- 对称差集 s ^ z ( A Δ B = A ∪ B − A ∩ B A \Delta B=A\cup B-A\cap B AΔB=A∪B−A∩B)
计算needles元素在haystack中出现的次数
found = len(set(needles) & len(haystack))
found = len(set(needles).intersection(haystack))
散列冲突
散列表及散列冲突解决方案
【PTA】【数据结构与算法】散列冲突
散列表给dict带来的优势和限制
- 键必须是可散列的
- 字典在内存上开销巨大:字典使用了散列表,而散列表必须是稀疏的,导致字典空间效率低
- 键查询很快
- 键的次序取决于添加顺序
- 往字典添加新键可能改变已有键的顺序
4.文本和字节序列
人类使用文本,计算机使用字节序列
- 字符的标识是码位
- 字符的具体表述取决于所用编码。编码是在码位和字节序列之间转换时使用的算法
- 码位转换成字节序列的过程是编码
.decode()
,把字节序列转换为码位的过程是解码.enode()
l = [0, 1]
print(l[0], l[:1], l[0]==l[:1]) # 0 [0] False
struct模块提供一些函数把打包的字节序列转换成不同类型字段组成的元组,还有一些函数用于执行反向转换把元组转换成打包的字节序列。struct模块能处理bytes、bytearray和memoryview对象。memoryview类不是用于创建或存储字节序列,而是共享内存,无需复制字节序列可以访问其他二进制序列、打包的数组和缓冲中的数据切片,如Python Imaging Libary(PIL)就是这样处理图像。
典型编码
- latin1(iso8859_1)
- cp437
- gb2312
- utf-8
- utf-16le
处理文本文件时要始终明确指定编码,一般用encoding=utf-8
- 保存文本前最好使用
normalize('NFC', user_text)
清洗字符串from unicodedata import normalize
- 另外两个较严格的规范化形式(NFKC和NFKD),K表示compatibility(兼容性),二者可能会损失或曲解信息,但可以为搜索和索引提供中间表述
- 大小写折叠就是把所有文本变小写,再做其他变换,对于只包含latin1字符的字符串s,
s.casefold()
的结果和s.lower()
一样 - 非ASCII文本的标准排序方式是使用
locale.strxfrm
函数 - PyUCA是Unicode排序算法的纯python实现
- 双模式API:提供的函数能接受字符串或字节序列为参数,然后根据类型进行特殊处理
第三部分 把函数视作对象
5.一等函数
- map函数返回一个可迭代的对象,里面的元素是把第一个参数(一个函数)应用到第二个参数(一个可迭代的对象)中各个元素上得到的结果,eg:
list(map(fun_name, range(10))
- 高阶函数(higher-order function)接受函数为参数,或者把函数作为结果返回。函数式语言会提供map、filter和reduce三个高阶函数。
- 归约函数:把某个操作连续应用到序列的元素上,累计之前的结果,把一 系列值归约成一个值。归约函数包括:sum 、reduce 、all 、any 等。
all(iterable):若iterable的每个元素都是真值返回True;all([])返回True
any(iterable):只要iterable中有元素是真值返回True;any([])返回False
from functools import reduce
from operator import add
print(reduce(add, range(100))) # 4950
print(sum(range(100))) # 4950
- lambda表达式会创建函数对象
- 若想判断对象是否可以调用,可使用内置的callable()函数,eg:
[callable(obj) for obj in (str, abs, 13)] # [True, True, False]
python数据模型文档列出了7种可调用对象
- 用户定义的函数
- 内置函数
- 内置方法
- 方法:在类的定义体中定义的函数
- 类
- 类的实例
- 生成器函数
计算差集然后排序,得到类的实例没有而函数有的属性列表sorted(set(dir(func))-set(dir(obj)))
- 仅限关键字参数只能通过关键字参数指定,它一定不会捕获未命名的定位参数。定义函数时若想指定仅限关键字参数,要把它们放到前面有 * 的参数后面。如果不想支持数量不定的定位参数,但是想支持仅限关键字参数,在签名中放一个 *
- 支持函数式编程的包:operator、functools
- 除了operator的reduce函数,operator模块中还有一类函数能替代从序列中取出元素或读取对象属性的lambda表达式:因此,itemgetter和attrgetter其实会自行构建函数
6.使用一等函数实现设计模式
7.函数装饰器和闭包
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。装饰器的特性:
- 能把被装饰的函数替换成其他函数
- 装饰器在加载模块时立即执行,而被装饰的函数只有在明确调用时才运行
闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。函数是不是匿名的没关系,关键是它能访问定义体之外定义的非全局变量
- 只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量
- python内置了三个用于装饰方法的函数:property、classmethod和staticmethod
- functools.lru_cache可以实现备忘录(memoization)功能。LRU指Least Recently Used,表明缓存不会无限制增长,一段时间内不用的缓存条目会扔掉
- functools.singledispatch装饰器可以把整体方案拆分成多个模块,甚至可以为无法修改的类提供专门的函数
第四部分 面向对象惯用法
8.对象引用、可变性和垃圾回收
前面看的模棱两可,这本书偏理论,实践的话感觉看博客就ok了,到此为止了