Learning Python
(4th Edition)
Python学习手册
第4版
Mark Lutz著
李军 刘红伟等译
O’REILLY Media, Inc
机械工业出版社
其实,我原先是不准备做这个笔记的,感觉编程这是技术性问题,多练即可,无奈大脑记性不佳,且技术之间的关系又没有搞清楚,故特作此笔记,以图理清脉络,加强记忆。
这篇笔记不会像以前那样详细,只是作为个人一种梳理、助记工具而已。
话说该作者挺为初学者着想的,全面详细,美中不足的是话有些重复。
一个多星期读完这本书,我好佩服自己啊!!!
类型和运算
- 程序由模块构成。
- 模块包含语句。
- 语句包含表达式。
- 表达式建立并处理对象。
核心数据类型
数字
字符串
列表
字典
字典视图
元组
文件
集合
动态类型
变量在赋值是创建,可以引用任何类型的对象,并且必须在引用之前赋值
变量是一个系统表的元素,拥有指向对象的连接的空间。
对象是分配的一块内存,有足够的空间去表示它们代表的值。
引用是自动形成的从变量到对象的指针。
类型属于对象,其头部有一个类型标识符和一个引用计数器。
引用计数器为零时,该对象的内存空间会被回收。
作为一种优化,Python会缓存不变的对象(通常是小的整数和小的字符串)并对其复用。
共享引用——多个变量名引用了同一个对象。当对象是不变对象时,修改其中一个变量不会影响到另外一个,但对象是可变对象时,原处修改则会影响到。
内置类型陷阱
赋值产生引用,而不是拷贝
重复能够增加层次深度
留意循环数据结构
不可变类型不可以在原处改变
语句和语法
赋值、表达式和打印
赋值
赋值语句执行时,python会建立相应的临时数据类型储存右侧变量原始的值
Python 3.0中的扩展序列解包,*号会使变量匹配剩下的内容
If测试和语法规则
布尔运算时,有短路规则,返回的是短路时所测试的对象。
While和for循环
循环else语句用于非break语句跳出,注意其缩进。
内置的range函数返回一系列连续增加的整数,可作为for中的索引。
zip函数返回并行元素的元祖列表,可用于for中遍历数个序列。
enumerate函数产生偏移值及偏移值处的值
迭代器和解析,第一部分
一般情况下,“可迭代的”指支持iter的一个对象,“迭代器”值iter所返回的一个支持next(I)的对象。
for循环开始时,会通过它传给iter内置函数,以便从中取得迭代器。
列表解析从语法上讲,源于集合理论表示法中的一个结构【难道是我在高中学的那个】。通常其迭代是以C语言的速度执行。
文档
#注释
dir函数
文档字符串:__doc__
写成字符串,放在模块文件、函数以及类语句的顶端的注释
PyDoc:help函数
提取文档字符串并且自动提取其结构化的信息,并将其格式化成各种类型的排列友好的报表。
PyDoc:HTML报表
函数
函数基础
def语句是实时执行的
作用域
作用域法则
内嵌的模块是全局作用域。
全局作用域的作用范围仅限于单个文件
每次对函数的调用都创建一个新的本地作用域
赋值的变量名除非声明为全局或非本地,否则均为本地
所有其他的变量名都快归纳为本地、全局或内置的。
变量名解析:LEGB原则
当在函数中使用为认证的变量名时,会搜索4个作用域,本地(L),上层函数的本地(E),全局(G),内置(B,__builtin__标准库模块),在第一个找到处停下。
当在函数中给一个变量名赋值时,Python总是创建或改变本地的变量名,除非已经声明为全局变量
在函数外给变量名赋值时,本地与全局是相同的。
global语句
全局变量如果在函数内被赋值的话,必须经过global声明
全局变量名在函数内部不经过声明也可被引用
作用域和嵌套函数
嵌套函数能够记住嵌套作用域的变量值,即使上层函数已经返回了。
nonlocal语句
将变量映射到上层函数作用域中的对应变量。
参数
在函数调用中,参数必去以此顺序出现:任何位置参数(value),后面跟着任何关键字参数(name=value)和*sequence形式的组合,后面跟着**dict形式。
在函数头部,参数必须以此顺序出现:任何一般参数(name),紧跟着任何默认参数(name=value),如果有的话,后面是*name的形式,后面跟着name或name=value keyword-only参数,后面跟着**name形式。
参数匹配步骤
- 通过位置分配非关键字参数
- 通过匹配变量名分配关键字参数
- 其他额外的非关键字参数分配到*name元祖中
- 其他额外的关键字参数分配到**name字典中。
- 用默认值分配给在头部未得到分配的参数。
函数的高级话题
尽量用循环,而不是递归,因为for循环会自动迭代。
函数注解,编写在def头部行。参数:注解,参数列表->注解。
匿名函数:lambda
是一个表达式,不是一个语句。
主体是一个单个的表达式,不是代码块。
迭代和解析,第二部分
列表解析通用结构 [expression for target1 in iterable1 [if condtion1] for target2 in iterable2 [if condition2]…]
重访迭代器:生成器
生成器函数:编写为常规的def语句,但使用yield语句一次返回一个结果,在每个结果之间挂起和继续他们的状态。yield语句编译为生成器,调用时,返回一个支持自动创建__init__方法的迭代器
生成器表达式类似于列表解析,返回按需产生结果的一个对象。括在圆括号中而不是方括号中。
模块
模块:宏伟蓝图
import如何工作
第一次导入指定文件时,会执行三个步骤。1、找到模块文件。2、编译成位码(需要时)。3、执行模块的代码来创建所定义的对象。
模块搜索路径
sys.path包括四个部分:
- 程序的主目录
- PYTHONPATH目录
- 标准链接库目录
- 任何.pth文件的目录
模块代码编写基础
import和from是赋值语句
import将整个模块对象赋给一个变量名
from将一个或多个变量名赋给另一模块中同名的对象
导入和作用域
函数绝对无法看见其他函数内的变量名,除非它在处于这个函数内
模块程序代码绝对无法看见其他模块内的变量名,除非明确地进行了导入
重载模块
reload函数强制已加载的模块代码重新载入并重新执行。新的代码的赋值语句会在适当的地方修改现有的模块对象。
模块包
Python代码的目录称为包
包和搜索路径设置
包所在的容器目录应在模块搜索路径中
__init__.py包文件
包导入路径中的每个目录内都必须有__init__.py文件
包首次导入时,会自动执行该文件
可为目录所创建的模块对象提供命名空间,包含所赋值的所有变量名
使用__all__列表,定义目录以from*语句形式导入时,需要导出什么。
包相对导入
Python 3.0中的变化
修改了模块导入搜索路径语义,以默认地跳过包自己的目录。到如只是检查搜索路径的其他组件。这叫做“绝对”导入。
扩展了from语句的语法,以允许显式地要求导入值搜索包的目录。这叫做“相对”导入。
高级模块话题
最小化from*的破坏:_X和__all__
_X会阻止from*导入此变量名
__all__列表列出了from*语句要导出的变量
Import语句和from语句的as扩展
若要重载from语句导入的变量,需先重载该模块(若该模块没有导入的话,需先导入,再重载),再重新执行from语句。
类和OOP
类代码编写基础
类产生多个实例对象
类对象提供默认行为
Class语句创建类对象并将其赋值给变量名
Class语句的赋值语句会创建类的属性
类属性提供对象的状态和行为
实例对象是具体的元素
像函数那样调用类对象会创建新的实例对象
每个实例对象继承类的属性并获得了自己的命名空间
在方法内对self属性做赋值运算会产生每个实例自己的属性
类通过继承进行定制
超类列在了类开头的括号中
类从其超类中继承属性
实例会继承所有可读取类的属性
每个object.attribute都会开启新的独立搜索
逻辑的修改是通过创建子类,而不是修改超类
类可以截获Python运算符
以双下划线命名的方法(__X__)是特殊钩子
当实例出现在内置运算时,这类方法会自动调用
类可覆盖多数内置类型运算
运算符覆盖方法没有默认值,而且也不需要
运算符可让Python的对象模型相集成
更多实例
步骤1:创建实例
编写构造函数
以两种方式使用代码:作为模块文件或作为脚本使用__name__运行测试代码
步骤2:添加行为方法
编写方法
封装,把操作逻辑包装到界面之和
步骤3;运算符重载
提供打印显示
步骤4:通过子类定制行为
编写子类
扩展方法,调用超类的方法
继承、定制和扩展
步骤5:定制构造函数
步骤6:使用内省工具
instance.__class__
object.__dict__
步骤7:把对象存储到数据库中
Pickle和Shelve
pickle:任意Python对象和字节串之间的序列化
dbm:实现一个可通过键访问的文件系统,以存储字符串
shelve:使用另外两个模块按照键把Python对象存储到一个文件中
类代码编写细节
运算符重载
构造函数和表达式:__init__和__sub__
索引和分片:__getitem__和__setitem__
拦截分片slice()
索引迭代:__getitem__
迭代器对象:__iter__和__next__
所有迭代环境会先尝试__iter__方法,再尝试__getitem__方法。
当迭代器返回自身时,只支持一个活跃的迭代器。反之,则可支持独立位置的多个活跃迭代器。
成员关系:__contains__、__iter__和__getitem__
优先级如上所示
属性引用:__getattr__和__setattr__
__getattr__拦截未定义的属性
__repr__和__str__会返回字符串表达形式
打印操作首先尝试__str__和str内置函数
右侧加法和原处加法:__radd__和__iadd__
只有+右侧对象是类实例,左侧不是时,才调用__radd__,其他所有情况下,由左侧对象调用__add__方法。
Call表达式:__call__
当调用实例时,使用该方法。
比较:__It__、__gt__和其他方法
布尔测试:__bool__和__len__
对象析构函数:__del__
类的设计
Python的OOP可概括三个概念:继承、多态和封装
应该吧程序代码写成预期的对象接口,而不是特定的数据类型。
OOP和继承:“是一个”关系
OOP和组合:“有一个”关系
OOP和委托:“包装”对象
类的伪私有属性
Class语句中开头有两个下划线,但结尾没有两个下划线的变量名自动扩张,_classname__x
方法是对象:绑定或无绑定
无绑定类方法对象:无self。通过对类进行点号运算从而获取类的函数属性。Python 3.0删除了该概念。
绑定实例方法对象:self+函数对。通过对实例进行全运算从而获取类的函数属性
多重继承:“混合”类
超类多于一个时,即为多重继承
传统类中,属性搜索深度优先
新式类(以及Python 3.0的所有类),属性搜索沿着树层次,广度优先
用__dict__列出实例属性
用dir列出继承的属性
类的高级主题
扩展内置类型
通过嵌入扩展类型
通过子类扩展类型
新式类
(Python 3.0)所有类的继承自object,所有对象都是object的实例
新式类变化
类和类型合并
继承搜索顺序
针对内置函数的属性获取
新的高级工具
__slots__:只有其列表中的变量名可赋值为实例属性
异常和工具
异常基础
异常编码细节
try/except/else语句
try下的代码代表主要动作,except定义异常的处理器,else子句提供无异常时的处理器
空的except子句捕捉一切异常,捕捉Exception的异常类似于此,但会忽略和系统退出有关的异常。
一旦捕捉了错误,控制权会在捕捉的地方继续下去,即try语句之下,没有直接的方式可以回到异常发生的地方。
try/finally语句
当控制权离开try代码块时,Python先执行finally代码块,然后才跳出。finally不会终止异常。
统一try/except/finally语句
raise语句
raise <instance>
raise <class> #Make and raise instance of class
raise 重新引发最近引发的异常,以传播已经捕获的异常。
Python 3.0异常链:raise from
assert语句(断言)
with/as环境管理器
with expression [as variable]: with-block
expression返回一个支持环境管理器协议(有__enter__和__exit__方法)的对象。其__enter__方法的返回值会赋值给variable
若有异常被引发,__exit__(type,value,traceback)方法被调用,若其返回值为假,异常会重新引发,以传播到with语句外。
没有异常时,__exit__(None,None,None)会被调用。
异常对象
基于类的异常的特点:提供类型分类;附件状态信息;支持继承。
异常的设计
嵌套异常处理器
关于sys.exc_info
有处理器处理时,返回(type、value和traceback)元组