published on in tech
tags: python

Fluent Python Reading Notes

1

  • dict() is slower than {}
  • list() is slower than ()
  • 需要符号查找(不能预先知道list()有没有被重定义)
  • 方法调用
  • 要检查有没有可迭代变量传入

2

特殊方法

  • __len__()
  • __getitem__()
  • __setitem__()
  • __iter__()

是为了被python解释器调用的,自己不用调用它们,所以没有A.__len__()方法,只有len(A)

3

自定义布尔值 -> 实现__bool__

4

  • __str__() : readable, 面向用户
  • __repr__() : unambigous, 面向DEV

5

The Zen of Python

    1. 实用胜于纯粹
    1. 不能让特例特殊到开始破坏规则

6

内置序列类型

  • 容器序列:任意类型,引用 - list, tuple, deque
  • 扁平序列:单一类型,值 - str(3.0), byte(3.0), bytearray, memoryview
  • 可变序列(MutableSequence):list, deque
  • 不可变序列(Sequence):tuple, str, byte

7

  • list comprehension = listcomps 列表推导
  • generator expression = genexps 生成器表达式

8

  • chr(65) -> A
  • ord(A) -> 65
  • unichr(12345) -> u'\u3039'

9

列表推导的作用只有一个: 生成列表

10

genexps优于listcomps

genexps遵守了迭代器协议,可以逐个产生元素,而不是先建立一个完整的列表,避免了额外的内存占用

11

元组拆包

  • a,b = (a,b)
  • a,b = b,a 两个变量值交换

12

*来处理剩下的元素

Python3: 平行赋值: a,b,*rest = range(5) -> (0,1,[2,3,4])

13

namedtupletuple创建实例消耗内存一样,因为字段名都存在类里:

  • _fileds类属性
  • _make(iterable)类方法
  • _asdict()实例方法

14

tuple没有__reversed__方法,但可以reversed(my_tuple)

15

切片赋值:改变list中连续多个值

    l = list(range(10))
    l[2:5] = [20,30]

-> l:[0,1,20,30,5,6,7,8,9]

16

__iadd__方法:就地加法

  • + = __iadd__
  • * = __imul__

17

id(object) -> return “Identity(内存地址)” of an object

  • += or *= 用在list上,不改变id
  • += or *= 用在tuple上,改变id,重新创建一个obj

18

如果一个方法对对象进行的是就地改动,那就应该返回None

19

  • list.sort -> 就地改
  • sorted() -> 新建一个列表返回

Example: sorted(fruits, key=len) sorted(fruits, key=lower)

20

bisect 二分查找

    1. bisect(haystack, needle)
    1. insort(seq, item) -> 插入后仍保持升序

21

字符串格式化

' '.format(a,b,c)

  • {0} -> 位置参数
  • {name} -> 关键字参数
  • {0[0]} -> 对list使用索引
  • {0:*(填充字符)>(右对齐)10(宽度)} -> 填充与格式化

22

array 场景->1000万个浮点数 deque 场景->需要频繁对序列进行FIFO

23

memoryview

24

numpy, scipy

25

collections.deque:线程安全、快速从两端添加、删除元素。

场景:存放最近使用的几个元素

    1. rotate(3):右边三个到左边。rotate(-4):左边四个到右边。
    1. appendleft():单个。extend, extendleft: 队列。

26

  • **散列表**是字典类型性能出众的根本原因。
  • collections.abc.Mapping的instance
  • 原子不可变数据类型str, byte和数值类型)
  • 散列值就是id()函数的返回值

27

字典创建方法

    1. a = dict(one=1,two=2,three=3)
    1. b = {'one':1,'two':2,'three':3}
    1. c = dict(zip(['one','two','three'],[1,2,3]))
    1. 字典推导 dictcomp

28

**dict.setdefault**处理不存在于dict中的key

29

**collections.defaultdict**是处理找不到key的一个选择

dd = defautdict(list),如果'new-key'不存在,dd['new-key']会执行:

    1. list()建新列表
    1. 这个新列表作为new-keykey,放入dd
    1. 返回这个列表的引用

30

__missing__只会被__getitem__调用

31

如果要自定义一个映射类型,应该继承collections.UserDict

32

字典的变种

    1. collections.OrderedDict: popitem方法 -> 删除并返回
    1. collections.ChainMap: 多个字典逐个查找
    1. collections.Counter:
    ct = Counter('abracadabra')
  • ->
    Counter({'a':5,'b':2,'r':2,'c':1,'d':1})
    1. UserDict用户涌来继承写子类
    1. TransformDict

33

不可变映射类型

  • types.MappingProxyType只读的映射视图

34

set -> 许多唯一对象的聚集;frozenset -> 不可变

集合

    1. 用于去重复:list -> set -> list
    1. 合集,交集,差集 -> 场景:元素出现次数

如果是空集必须写成set()

35

散列表:稀疏数组(总有空白元素的数组) key:value -> 一个表元

因为所有表元大小一致,所以可以通过偏移量来读取某个表元

hash()来计算散列值

36

存放数量巨大的记录用tuplenamedtuple

dict 散列 -> 内存消耗大

37

dict空间时间 -> 读取快内存消耗大

Python解释器可能为字典扩容 -> 导致新建一个更大的散列表

38

集合 set

    1. 元素必须可散列
    1. 很耗内存
    1. 高效查找
    1. 次序取决于添加次序
    1. 添加元素可能改变已有元素次序(37中的扩容)

39

字符的最佳定义是Unicode字符

Python3str对象中获取的元素是Unicode字符

字符的具体表述取决于所用的编码

  • 编码:码位 -> 字节序列
  • 解码:字节序列 -> 码位(字符的标识)

40

字面量:双眼所见

41

utf_8 的别名:utf8, u8, utf-8

42

编码种类

  • latin1(iso8859-1):是其他编码的基础
  • cp1252:Windows制定的latin1的超集,添加了有用的符号
  • gb2312:用于编码简体中文的陈旧标准
  • utf-8:web最常见的8位编码,与ASCII兼容

43

Python3 中默认使用 utf8 编码源码

44

判断编码

以下是肉眼方法

    1. HTTPXML,包含明确指明内容编码的首部
    1. 包含大于127的字节值,那么就不是ASCII
    1. 如果 b'\x00' 经常出现,那么就不是8位编码方案

统一字符编码侦测包:Chardet

45

Python中,函数是一等对象

    1. 运行时创建
    1. 能赋值给变量或数据结构中的元素
    1. 能作为参数传给函数
    1. 能被return

46

__doc__用于生成对象的帮助文本

47

函数式编程:特点之一是使用高阶函数

高阶函数:

    1. 接受函数作为参数
    1. 把函数作为结果返回

48

map, filter 和 reduce 在Python3中已经不是内置函数 被 listcompsgenexps 所替代(可读性原因)

49

匿名函数:lambda

50

使用callable()函数来判断一个对象能否调用

51

用户定义可调用类型,只需实现实例方法__call__

52

函数内省

53

函数有但一般对象没有的特殊方法:

  • __annotations__
  • __call__:实现()运算符

54

函数注解只存放在__annotations__属性里,Python不做检查、验证、强制…

注解只是元数据,可以供IDE、框架和装饰器工具使用

55

函数式编程

    1. operator -> itemgetter(get下标),attrgetter(get name)
    1. functools 内包含 reduce函数,partial冻结参数,partialmethod

Example:

    triple = partial(mul,3)
    triple(7) -> 21

56

globals() 策略模式

57

命令模式:是回调机制的面向对象替代品

58

nonlocal关键字。

装饰器 目的是增强函数

59

元编程 -> 在运行时改变程序的行为

装饰器的两大特性

    1. 能把装饰的函数替换成其他函数
    1. 在加载模块时立即执行

60

装饰器 在被装饰的函数定义之后立即运行。通常是在导入时(加载模块时)

61

变量作用域规则

    b = 6
    def f2(a):
        print(a)
        print(b)
        b = 9
    
    f2(3)

输出 3, local variable 'b' referenced before assignment

Python编译函数定义体时,它判断b是局部变量,因为在函数中给它赋值了。生成的字节码证实了这种判断,Python会尝试从本地环境获取b。后面调用f2(3)时,f2的定义体会获取并打印局部变量a的值,但是尝试获取局部变量b的值时,发现b没有绑定值。

这不是缺陷,而是设计选择:Python不要求声明变量,但是假定在函数定义体中赋值的变量是局部变量。

如果在函数中赋值时想让解释器把b当成全局变量,要使用global声明。

62

闭包 不等同于 匿名函数

闭包是延伸了作用域的函数,函数是不是匿名的没有关系,关键是它能否访问定义体之外定义的全局变量