《流畅的python》读书笔记

流畅的python

第1章 python数据模型

---1.1 一摞Python风格的纸牌

  • 特殊方法,即__method__,又被称为魔术方法(magic method)或者双下方法(dunder-method).html

  • 特殊方法的存在是为了被python解释器调用的python

  • collections.namedtuple用于构建一个只有少数属性可是没有方法的对象算法

  • 经过实现__getitem__,可使对象有[]操做,支持切片操做,可迭代编程

  • for i in x:其实是用了iter(x),而这个函数背后则是x.__iter__()方法设计模式

  • random.choice能够从一个序列中随机选出一个元素数组

  • 经过实现一个针对对象的key函数,就能够对该对象进行排序安全

  • 实现特殊方法来利用python模型的好处是:多线程

    1. 类有统一的标准操做名称闭包

    2. 能够更加方便地利用python的标准库app

---1.2 如何使用特殊方法

  • __repr__使对象有一个字符串表示形式.它与__str__的区别是,后者在str()函数或是print()函数被使用.若是一个对象没有__str__函数,python会用__repr__代替.

  • __add__, __sub__, __mul__, __truediv__对应+-*\四种算数运算符

  • 默认状况下,自定义的类的实例总被认为是True,除非对__bool____len__有实现.bool(x)会先调用前者,再调用后者,0为False,不然为True.

---1.3 特殊方法一览

第2章 序列构成的数组

---2.1 内置序列类型概览

  • 容器序列能够存放不一样类型的数据,存放的是所包含的对象的引用:list,tuple,collections.deque

  • 扁平序列只能容纳一种类型,存放的是值,是一段连续的内存空间:str,bytes,bytearray,memoryview,array.array

  • 可变序列:list,bytearray,array.array,collections.deque,memoryviw

  • 不可变序列:tuple,str,bytes

---2.2 列表推导和生成器表达式

  • 最好只用列表推导来生成列表,而用生成器表达式来初始化元组,数组或其余序列类型.由于生成器表达式能够逐个产出元素,更节省内存.

---2.3 元组不只仅是不可变的列表

  • 元组除了用做不可变列表,还能够用于没有字段名的记录,其中的每一个元素都存放了记录中一个字段的数据和位置

  • 元组拆包(例如平行赋值,函数返回多个值)可应用到任何可迭代对象上,只要被可迭代对象中的元素数量和接受这些元素的元组的空挡数一致.

  • *前缀的变量名能够处理元组拆包中的剩余元素

  • collections.namedtuple能够用来构建一个带字段名的元组和一个有名字的类.

  • 元组支持除了增减元素相关的方法以外的其余全部列表的方法.

---2.4 切片

  • 若是赋值的对象是一个切片,那么赋值语句的右侧必须是个可迭代对象,即便是单个值也要转换成可迭代序列.

---2.5 对序列使用+和*

  • +和*操做不会修改序列,而会新建一个包含一样类型数据的序列做为拼接的结果.

  • 注意:若是a*n中的a是其余可变对象的引用的话,它们会指向同一个可变对象.正确的方式是[[] * m for i in range(n)]

---2.6 序列的增量赋值

  • +=(*=)对应的特殊方法是__iadd__(__imul__),若是没有被实现,解释器会退一步调用__add__.可变序列通常都实现了__iadd__

  • 可变序列的增量赋值会直接追加到原序列上,不可变序列则会建立一个新对象,把原来的元素复制进去后再追加新的元素,所以效率很低.可是str是不可变序列中的一个例外.

  • 下列语句会成功改变t的值,同时抛出异常.所以最好不要把可变对象放在元组里.

t= (1, 2, [3, 4])
t[2] += [5,6]

---2.7 list.sort方法和内置函数sorted

  • list.sort方法会就地排序.所以它返回None,这也是python中函数或者方法对对象进行就地改动时的惯例.

  • sorted能够接受任何可迭代对象做为参数,并新建一个列表做为返回值.

---2.8 用bisect来管理已排序的序列

  • bisect模块提供了二分查找算法.包含两个主要函数:bisect用于搜索,insort用于插入.

---2.9 当列表不是首选时

  • 若是须要一个只包含数字的列表,那么数组(array.array)比list更高效.建立数组须要一个类型码,表示在底层的C语言应该存放怎样的数据类型.以二进制文件读写数据会显著提升效率.

  • 内存视图(memoryview)能在不复制内容的状况下操做同一个数组的不一样切片.

  • 双向队列(collections.deque)是一个线程安全,能够快速从两端添加或者删除元素的数据类型.能够在多线程程序中看成先进先出的栈使用.

第3章 字典和集合

---3.1 泛映射类型

  • 若是一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是不变的,并且这个对象须要实现__hash____eq__方法.若是两个可散列对象是相等的,那么它们的散列值必定同样.str,bytes,数值类型,frozenset以及只包含可散列类型的元组都是科三咧类型.

---3.2 字典推导

  • 字典推导相似于列表推导,能够从任何以键值对做为元素的可迭代对象中构建出字典.

---3.3 常见的映射方法

  • 当须要经过查找来插入新值的时候,dict.setdefault比if语句要高效不少.

---3.4 映射到弹性键查询

  • collections.defaultdict为不存在的键提供了一个默认值.

  • 还能够经过继承dict并实现__missing__方法来处理键不存在的状况.

---3.6 子类化UserDict

  • 若是须要创造自定义映射类型,继承UserDict是一个很好的选择.

---3.7 不可变映射类型

  • 若是不能让用户错误地修改某个映射,可使用types.MappinrgProxyType.将一个字典传给它,它会动态返回原字典的改动,可是不能经过它作任何修改.

---3.8 集合论

  • 集合的本质是许多惟一对象的汇集,能够用于去重.

  • 集合中的元素必须是可散列的,set自己是不可散列的,可是frozenset是能够散列的.

  • 集合包含不少中缀运算符,|取合集,&取交集,-取差集.

  • 一样能够用集合推导式来建立集合

---3.9 dict和set的背后

  • 散列表是一个稀疏数组,python会设法保证大概有1/3的表元是空的.在快要达到阈值的时候,原有的散列表会被复制到一个更大的空间里面.

  • 散列值应该在索引空间中尽可能分散,所以越是类似但不相等的对象,它们散列值的差异应该越大.实际运用中,多数搜索过程当中并不会有冲突发生.

  • 字典在内存上的开销巨大,所以最好使用元组来存放大量数据.

  • 字典键的次序取决于添加顺序,往里面添加新键可能会改变已有的次序,所以不要对字典同时进行迭代和修改.

  • 上述特色,对集合也几乎都适用.

第4章 文本和字节序列

---4.1 字符问题

  • Unicode中字符的标识(码位)是0~1114111的数字,以4~6个16进制数字表示,并且加前缀"U+".字符的具体表述取决于所用的编码.

---4.2 字节概要

  • python3的bytes或bytearray对象的各个元素是介于0-255之间的整数,可是它们的切片还是同一类型的二进制序列.

  • 二进制序列可能以ASCII字符自己,转义序列和十六进制转义序列三种形式显示.

---4.3 基本的编解码器

  • python自带超过100种编解码器.

  • UTF编码设计的目的就是处理每个Unicode码位.

---4.4 了解编码问题

  • 多数非UTF编解码器只能处理Unicode字符的一小部分,此时在编码时会抛出UnicodeEncodeError.

  • 不是每个字符序列都是有效的UTF编码,若是没法转换时会抛出UnicodeDecodeError.

---4.5 处理文本文件

  • 处理文本的最佳方法是:尽早把输入解码成字符串,尽可能晚地把字符串编码成字节序列.python3的open,read和write方法已经帮忙实现了这个原则.

  • 须要在多台设备中或多种场合下运行的代码,打开文件时必定要明确传入encoding,由于不一样设备的默认编码可能不一样.

---4.6 为了正确比较而规范化Unicode字符串

  • Unicode有组合字符(变音符号和附加到前一个字符上的记号),因此字符串比较起来很复杂.问题点解决方案是使用unicodedata.normaliza进行规范化.通常选取NFC

第5章 一等函数

---5.1 把函数视做对象

  • 在python中,函数是一等对象,函数对象自己是function类的实例.

---5.2 高阶函数

  • 接受函数为参数,或者把函数做为结果返回的函数是高阶函数,例如map,filter.最好使用列表推导或生成器表达式替代它们.

---5.3 匿名函数

  • lambda能够建立匿名函数,通常用于做为参数传给高阶函数.它只是语法糖,实际上也会建立函数对象.

---5.5 用户定义的可调用类型

  • 任何python对象均可以被调用,只要实现了__call__

---5.7 从定位参数到仅限关键字参数

  • 在函数参数中使用*和**能够展开参数并捕获.若是把参数放在前面有*的参数后面,就成为了仅限关键词参数.

---5.10 支持函数式编程的包

  • operator模块为多个算数运算符提供了对应的函数,还有itemgetter和attrgetter能够从序列中取出元素.

第6章 使用一等函数实现设计模式

---6.1 案例分析:重构"策略"模式

  • 策略模式是指:定义一系列算法,把它们一一封装起来,而且使它们能够相互替换.

  • 能够把策略函数做为参数传入对象,简化策略模式.

  • 可使用globals来获取全部的策略函数,但要求策略函数有一个统一命名规则.

第7章 函数装饰器和闭包

---7.1 装饰器基础知识

  • 装饰器是可调用的对象,其参数是被装饰的函数.装饰器一般把函数替换成另外一个函数.

---7.2 Python什么时候执行装饰器

  • 函数装饰器在导入模块时当即执行,而被装饰的函数只在明确调用时运行.

---7.3 使用装饰器改进"策略"模式

  • 在上一章中,若是用promos列表存储策略函数,可能会存在忘记添加的问题.若是使用一个装饰器函数来将策略函数添加到列表中,并将每个策略函数都用这个装饰器函数来装饰,就能够解决上述问题.

---7.4 变量做用域规则

  • python编译函数的定义体时,会将在函数中赋值的变量判断为局部变量.若是须要让解释器把变量当成全局变量,须要在函数内用global声明.

---7.5 闭包

  • 闭包指延伸了做用域的函数,其中包含函数定义体中引用,可是不在定义体中定义的非全局变量.

  • 未在本地做用域中绑定的变量叫自由变量.

  • 闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义做用域不可用了,可是仍能使用那些绑定.

---7.6 nonlocal声明

  • 在7.5的例子中,实际上利用了列表是可变的对象.若是想用两个数值count和total来实现,那么在+=时至关于赋值,因而它们会变成局部变量,而不是自由变量.

  • 为了解决这个问题,须要使用nonlocal声明.它的做用是把变量标记为自由变量.

  • 另外一种实现方式是,把内部函数须要修改的变量存储伟可变对象的元素或属性,而且把那个对象绑定给一个自由变量.

---7.8 标准库中的装饰器

  • functools.lru_cache能够把耗时的函数的结果保存起来,避免传入相同的参数时重复计算.能够用于优化递归算法.注意lru_cache使用字典存储结果,因此被它装饰的函数的全部参数都必须是可散列的.

  • functools.singledispatch能够把总体方案拆分红多个模块,也就是将普通函数变成泛函数:根据第一个参数的类型,以不一样方式执行相同操做.

---7.9 叠放装饰器

  • 把@d1和@d2两个装饰器按顺序应用到f函数上,至关于f=d1(d2(f))

---7.10 参数化装饰器

  • 经过装饰器工厂函数,将真正的装饰器放在内部,就能够接受除了函数之外的参数.

第8章 对象引用,可变性和垃圾回收

---8.1 变量不是盒子

  • 对于面向对象语言中的引用式变量,它们是附加在对象上的标注,而不是盒子.

  • 建立对象以后才会把变量分配给对象.对象在赋值语句右边建立或获取,而后左边的变量才会绑定到对象上.

相关文章
相关标签/搜索