0% found this document useful (0 votes)
48 views138 pages

Python 学习笔记

Uploaded by

hancanr
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
48 views138 pages

Python 学习笔记

Uploaded by

hancanr
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 138

目 录

致谢

python 学习笔记

01. Python 的动态类型介绍

02. 数字类型

03. Set类型

04. 字符串

05. 列表

06. 元组

07. 通用序列

08. 字典

09. 文件

10. 语句和语法

11. 赋值语句

12. 打印

13. if语句

14. while、for循环

15. 迭代器和生成器

16. 文档

17. 函数

18. 作用域

19. 参数

20. 函数的高级特性

21. 模块

22. 模块包

23. 模块高级用法

24. 类 class

25. Python命名空间

26. 运算符重载

27. 类的设计模式

28. 类的高级主题

29. 异常

30. 异常对象

31. Unicode与字节串

32. 管理属性

33. 装饰器

34. 元类

本文档使用 书栈(BookStack.CN) 构建 -2-


35. Python 执行细节

本文档使用 书栈(BookStack.CN) 构建 -3-


致谢

致谢

当前文档 《python 学习笔记》 由 进击的皇虫 使用 书栈(BookStack.CN) 进行构建,生成于 2018-05-


29。

书栈(BookStack.CN) 仅提供文档编写、整理、归类等功能,以及对文档内容的生成和导出工具。

文档内容由网友们编写和整理,书栈(BookStack.CN) 难以确认文档内容知识点是否错漏。如果您在阅读文档
获取知识的时候,发现文档内容有不恰当的地方,请向我们反馈,让我们共同携手,将知识准确、高效且有效地传递
给每一个人。

同时,如果您在日常工作、生活和学习中遇到有价值有营养的知识文档,欢迎分享到 书栈(BookStack.CN) ,
为知识的传承献上您的一份力量!

如果当前文档生成时间太久,请到 书栈(BookStack.CN) 获取最新的文档,以跟上知识更新换代的步伐。

文档地址:http://www.bookstack.cn/books/python_learning_notes

书栈官网:http://www.bookstack.cn

书栈开源:https://github.com/TruthHun

分享,让知识传承更久远! 感谢知识的创造者,感谢知识的分享者,也感谢每一位阅读到此处的读者,因为我们
都将成为知识的传承者。

本文档使用 书栈(BookStack.CN) 构建 -4-


python 学习笔记

python 学习笔记
本文档是《learning python》中文第四版的学习笔记,是由我个人做的纸质读书笔记而来。

文档PDF版本可能会滞后于项目进度
纸质笔记可以从百度网盘下载:http://pan.baidu.com/s/1miEkaBu
个人所作的所有笔记也可以从百度网盘下载:http://pan.baidu.com/s/1boSzlx1

目标
这份笔记的目标是为了给出一份比较精炼,但是又要浅显易懂的Python教程。《Python学习手册》中文第四版虽然
比较简单,但是措辞比较罗嗦,而且一个语法点往往散落在多个章节,不方便读者总结。

我在做笔记时,将一个知识点的内容都统筹在一个章节里面,因此提炼性大大提高。而且还有《Python学习手册》中
文第四版的翻译在某些章节(可能难度较大?)措辞可能前后矛盾。当知识点提炼之后就能够很快的找到一些难以理
解的概念的上下文,方便吃透这些难点。

内容介绍
本文档一共35个章节。分别为:

1. Python的动态类型介绍
2. 数字类型
3. Set类型
4. 字符串
5. 列表
6. 元组
7. 通用序列
8. 字典
9. 文件
10. 语句和语法
11. 赋值语句
12. 打印
13. if语句
14. while/for循环
15. 迭代器和生成器
16. 文档
17. 函数
18. 作用域
19. 参数
20. 函数的高级特性
21. 模块
22. 包

本文档使用 书栈(BookStack.CN) 构建 -5-


python 学习笔记

23. 模块的高级用法
24. 类class
25. Python名字空间
26. 运算符重载
27. 类的设计模式
28. 类的高级主题
29. 异常机制
30. 异常对象
31. Unicode与字节串
32. 管理属性
33. 装饰器
34. 元类
35. Python的执行

基础知识
看这份笔记最好有面向对象的概念。因为这份文档里大量使用了面向对象的术语,比如”实例对象“、”类对象“、”名
字空间“等等。如果没有面向对象的概念,将会对理解后面的大部分章节有困难。

对于Python新手,这份文档难度不大。但是如果您对Python有一定的熟练程度,您会发现这份文档行云流水,一气
呵成,阅读起来就没有停顿感。如果您对于Python完全陌生,那么本文档阅读过程中,对于一些复杂的概念建议您动
手练手而不要完全依赖于文档中的示例。

补充
本文档所有的Python代码都是基于Python 3.5 64bit,在Python IDLE上运行的效果。操作系统为 Win7
64bit Professional

本文档由 markdown 语法制作而成,其中使用到了扩展的非标准 markdown 语法(流程图功能)。本人制作了


一份 markdown 教程,项目地址
在:https://github.com/huaxz1986/markdown_documentation_chinese .你也可以在百度网盘下
载PDF版本:http://pan.baidu.com/s/1qWXaJRA

本文档编辑器使用 cutemarked 软件。它是一款开源的 markdown 文件编辑器,支持流程图扩展与 LATEX 数


学公式扩展。本人fork了一个版本,然后改装成了一个本地化版本,项目地址:
https://github.com/huaxz1986/CuteMarkEd 。你也可以在百度网盘下载我编译打包好的版本(解压缩
直接双击运行即可),地址:http://pan.baidu.com/s/1dEqupZ7 ,运行环境为 Win7 64bit
Professional

cutemarked 原版需要联网下载所需要的脚本文件,但是由于国内的网络问题,以及存在断网情况下使用等特殊需求
,我将它改造成了一个无需联网的本地化版本

本文档仅用于个人学习目的,未经许可不得用于商业目的,转载请注明出处

email: [email protected]

来源(书栈小编注)
本文档使用 书栈(BookStack.CN) 构建 -6-
python 学习笔记

https://github.com/huaxz1986/python_learning_notes

本文档使用 书栈(BookStack.CN) 构建 -7-


01. Python 的动态类型介绍

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

Python 的动态类型介绍
1.在Python中没有变量的声明以及变量的类型:

变量的类型实在运行过程中自动决定的,而不是通过声明来决定的
变量首次赋值时,创建它。之后的再次赋值会改变该变量的值

2.类型的概念是存在于对象中而不是变量名中。变量名是通用的,它只是在特定时刻引用某个特定的对象而已。

当变量出现在表达式中时,它会马上被当前引用的对象所代替
任何变量名在使用前必须明确地赋值。用未赋值的变量会产生错误。

在Python内部,变量实际上是指向对象内存空间的一个指针

1. graph LR
2. id1(变量名)-->|引用|id2(对象);

3. style id2 fill:#ccf,stroke:#f66,stroke-width:2px,stroke-dasharray: 5, 5;

3.每个表达式生成的结果,python都创建了一个新的对象去表示这个值

4.Python对象缓存了不变的对象并对其进行复用,如小整数和小字符串。但是逻辑上看,每个表达式的结果值都是不
同的对象,占用不同的内存。

5.每个对象都有两个标准的头部信息:

类型标志符:标识了该对象的类型(见第10条)
引用计数器:决定了是否可以回收这个对象(见第8条)
引用计数器记录了当前指向该对象的引用的数目。一旦它为0,
则该对象的内存空间就会自动回收

6.给一个变量赋新值,并不是替换原始的对象,而是让这个变量去引用完全不同的一个对象。

7.多个变量名引用同一个对象时,称为共享引用:

1. graph LR
2. id1(变量a) -->|引用|id2(对象)

3. id3(变量b) -->|引用|id2(对象)
4. style id2 fill:#ccf,stroke:#f66,stroke-width:2px,stroke-dasharray: 5, 5;

在共享引用中,对象的原地修改操作会影响到所有的引用该对象的变量

共享引用的多个变量之间, is 比较的结果为 True

== 操作符比较的是两个变量引用的对象是否具有相同的值

is 操作符比较的是两个变量是否引用的是同一个对象

本文档使用 书栈(BookStack.CN) 构建 -8-


01. Python 的动态类型介绍

8. sys 模块的 getrefcount 函数会返回对象的引用次数,如:

这里可以看到Python对象缓存了不变的对象并对其进行复用

9.动态类型是Python多态的基础,因为没有类型约束。Python的多态是 x.method 的方法运行时, method 的意


义取决于 x 的类型,属性总是在运行期解析。

10.查看对象的类型用 type() 函数,如:

11.Python提供了几种方法来查看对象的属性以及详细信息:

dir(x):查看x所指对象的属性
help(x.attr):查看x所指对象的attr属性
help(x):查看x所指对象的详细信息

12. None 对象是一个特殊的Python对象,它总是 False ,一般用于占位。它有一块内存,是一个真正的对象。它


不代表未定义,事实上它有定义。

None 是所有函数和方法的默认返回值

13.Python中,任何东西都是对象类型(见第10条),类型本身也是对象类型:

type(x) 返回变量 x 指向对象的类型


isinstance(x,typename) 用于测试 x 所指对象是否是 typename 类型
调用类型名是对这些对象构造函数的调用,而不仅仅是类型转换

14.基本上在代码中进行类型检查是错误的,着破坏了代码的灵活性,限制了代码的类型。Python代码不关心特定的
数据类型,只关心接口,这就是Python的多态设计

一个操作的意义取决于被操作对象的类型。同样的操作对不同的对象来说意义可能不同,前提是该对象支持该操

若对象不支持某种操作,则Python会在运行时检测到错误并自动抛出一个异常

15.Python可以在旧版本中开启新版本的特性,只需用在模块文件中使用:
from __future__ import 特性名

本文档使用 书栈(BookStack.CN) 构建 -9-


02. 数字类型

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

数字类型
1.Python的数字常量:

整数: 1234 , -1234 , 0 。Python支持无穷整数大小

Python3中,整数不再区分一般整数与长整数

Python2.7中整数分为一般整数(32位)与长整数(支持无穷整数大小)。
整数以 l 或者 L 结尾时为长整数。当整数值超过32位时自动转换为长整数

浮点数: 1.23 , 1. , .3 , 3.14e-10 , 4E10 , 4.0e+10

八/十六/二进制整数: 0o177 (小写的字符 o 或者大写的 O ), 0x9ff , 0b101010


对于Python2.7来讲,八进制整数为 0177 (没有小写的字符 o )

复数: 3+4j , 3.0+4.0j , 3j , 3J

2.数字类型转换:

hex(intx) 、 oct(intx) 、 bin(intx) 、 str(intx) 将整数 intx 转换成十六/八/二/十进制表示的字符串


int(strx,base) 将字符串 strx 根据指定的 base 进制转换成整数。 base 默认为10
float(strx) 将字符串 strx 转换成浮点数
complex(num_real,num_imag) 创建一个复数,实部为数字 num_real ,
虚部为数字 num_imag

3.混合类型表达式中,Python先将被操作对象转换成其中最复杂的操作对象的类型。

整数与浮点数混合操作时,将整数自动转换成浮点数
浮点数与复数混合操作时,将浮点数自动转换成复数
也可以通过 int() , float() ,以及 complex() 执行手动转换

4.Python允许执行连续比较,且比较链可以任意长:

a<b<c 结果等同于 a<b and b<c

a<b>c 结果等同于 a<b and b>c


这二者并不完全等价,因为 a<b<c 中的表达式 b 只需要计算一次;
而 a<b and b<c 中的表达式 b 需要计算两次

5.Python的除法 x/y 在Python3和Python2.7中不同

本文档使用 书栈(BookStack.CN) 构建 - 10 -
02. 数字类型

Python3中的除法保留小数部分,无论是整数除法还是浮点除法
Python2.7中的除法:对整数除法会截取整数部分放弃小数部分,对小数除法会保留小数部分

Python中还有一种除法:Floor除法 x//y ,它会将结果向下取整到不大于它的最大整数

即使是浮点的Floor除法,结果也是取整的浮点数

6.将字符串转为整数除了用 int() 函数外,也可以通过 eval() 函数将字符串转为整数;


整数转字符串除了用 str()/hex() 等函数外,也可以用格式化字符串。

7.Python支持将整数当作二进制位串对待的位操作。

8.Python支持许多对数字处理的内置函数与内置模块:

内置函数位于一个隐性的命名空间内,对应于 builtins 模块(python2.7叫做


__builtins__ 模块)

math 模块:如 math.pi , math.e , math.sqrt ….


内置函数,如 pow() , abs() ,…

本文档使用 书栈(BookStack.CN) 构建 - 11 -
02. 数字类型

9. random 模块提供的工具可以生成0~1之间的随机浮点数、两个数字之间的任意整数、
序列中的任意一项。

10.浮点数缺乏精确性,因为存储浮点数的空间有限(这是硬件相关的内存缺陷),
可以用 Decimal 对象解决精度问题。
Decimal 对象来自于 decimal 模块,它类似于浮点数,但是有固定的位数和小数点,
因此是固定精度的小数。相对于浮点数,它带来了微小的精度损失。

设置全局的精度和舍入模式: decimal.getcontext().prec=5 ,将全局精度设置为小数点后5位


可用整数、浮点数、字符串初始化 Decimal 对象

11.Python中的分数类型是 Fraction 对象,来自于 fractions 模块。

Fraction 对象以一个分子,一个分母初始化: Fraction(1,4) ;


也可以从浮点数或者浮点数的字符串初始化: Fraction('0.25') , Fraction(0.25)

Fraction 对象能保证精确性,且能自动简化结果

从浮点数产生分数有两个方法:

调用 Fraction 的构造函数: Fraction(0.25)

用 Fraction.from_float() 函数: Fraction.from_float(0.25)

从浮点产生分数时,可以通过 .limit_denominator() 限制分母的最大值。

12.浮点数有个 as_integer_ratio() 方法,能生成 (分子,分母) 的元祖。

本文档使用 书栈(BookStack.CN) 构建 - 12 -
02. 数字类型

13.整数、浮点数、 Fraction 、 Decimal 可以混合运算

Fraction 与 Decimal 不可以混合运算

1. graph LR
2. id1(整数)-->|提升|id2(浮点数);
3. id2(浮点数)-->|提升|id3(Fraction);

4. id2(浮点数)-->|提升|id4(Decimal);

14.Python中的布尔类型为 bool ,它只有两个值 True 和 False 。


True 和 False 是预定义的内置变量名,其在表达式中的行为与整数1和0是一样的。实际上他们就是内置
的 int 类型的子类。

15. 真 和 假 是Python中每个对象的固有属性:每个对象不是 真 就是 假 。
该属性可以用于 任何需要 bool`值的地方。

一个数如果不是0,则为真;如整数 0,浮点数 0.0 都是 假

其他对象如果非空,则为真;如空字符串 "" 为 假 ,空字典 {} 为 假 ,


空列表 [] 为 假 , None 对象为 假

本文档使用 书栈(BookStack.CN) 构建 - 13 -
03. Set类型

Set类型
1.Python的 set 类型是集合内元素值唯一、元素值不可变的无序集。
set 类型并不要求其集合内各个元素都是相同类型。

唯一: set 类型的集合内元素的值唯一。


如果有两个变量指向同一个对象,则在 set 集合内只会出现一次
如果有两个对象值相等,则在 set 集合内也只会出现一次
不可变:一旦创建完毕,则 set 集合内的各个元素值不能修改。
本质上 set 通过元素值的哈希值来判断唯一性

2. set 本身的性质有:

set 对象是可迭代对象
set 对象可以增长或缩短
set 对象可能包含各种类型的对象作为元素
创建 set 对象的方法为调用 x=set(iter) ,其中 iter 为任何序列或可迭代对象

3. set 对象有以下操作:

成员关系: e in x (返回 bool 值)


差集: x-y (返回新的 set 对象)
并集: x|y (返回新的 set 对象)
交集: x&y (返回新的 set 对象)
对称差集: x^y (返回新的 set 对象)
对称差集:并集中去掉交集的那部分

判定x是否是y的超集: x>y (返回 bool 值)


判定x是否是y的子集: x<y (返回 bool 值)

本文档使用 书栈(BookStack.CN) 构建 - 14 -
03. Set类型

3. set 的方法有:

.add(item) :向 set 中插入一项,原地修改(返回 None )。其中 item 为待插入项


.update(iter) :求并集,原地修改(返回 None )。其中 iter 为任何可迭代对象
.remove(item) :向 set 中删除一项,原地修改(返回 None )。其中 item 为待删除项

.intersection(iter) :求交集,返回新的 set 对象。其中 iter 为任何可迭代对象

注意这里与表达式中的 set 交集、并集操作的区别:


表达式中的 set 交集、并集操作要求两个对象均为集合

4. set 对象是可迭代的,因此可用于 len() 函数, for 循环,以及列表解析中,但是因为是无序的所以不支持


索引和分片操作。

5.Python3中, set 常量可以用大括号创建: {1,2,3,4} 等价于 set([1,2,3,4]) 。

空 set 必须由 set([]) 创建, {} 为空字典而不是空 set

Python3中, set 的字符串表示为 {...} 形式,而不是 set([...]) 形式

6. set 只能包含不可变的对象,因此列表对象、字典对象、 set 对象均不能作为 set 的元素。

元组和字符串可作为 set 的元素

本文档使用 书栈(BookStack.CN) 构建 - 15 -
03. Set类型

7. frozenset 是 set 的子类型,它与 set 的唯一区别是:


frozenset 对象可以作为 set 的元素。

8. set 的解析构造(在Python3之后): {x**2 for x in iter} ,其中 iter 为任何可迭代对象。

本文档使用 书栈(BookStack.CN) 构建 - 16 -
04. 字符串

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

字符串
1.Python的字符串是一种不可变序列,它包含的字符存在从左到右的位置顺序。
不可变是指:字符串创建以后无法修改它的内容。

序列包括:字符串、列表、元组

字符串由一对双引号或者单引号包围,二者无任何区别
如果希望字符串中包含引号,则有两种办法:

最外围的引号和字符串中的引号使用不同的格式。如 "who's name is Tex?"


采用转义字符。如 who\'s name is Tex?'

空字符串是一对引号之间不包含内容: ""

Python自动在任意的表达式中合并相邻的字符串常量
在字符串中反斜杠代表转义序列,每个转义序列代表一个字符(不一定能够打印出来):
\\ 转义反斜杠本身
\n 为换行符
\0 为 \0x00 (不是字符串结尾)
\xhh 为十六进制值
\o00 为八进制值
\uhhhh 为16位Unicode的十六进制值
\Uhhhhhhhh 为32位Unicode的十六进制值
Python中 \0 并不去结束一个字符串,Python会在内存中保持整个字符串的长度和文本
Python会以十六进制显示非打印的字符
若无合法的转义编码识别 \ 以及之后的字符,则Python会在最终的字符串中保留 \

2.raw 字符串是一种特殊的字符串。当 r 出现在字符串起始引号之前时,为 raw 字符串。这种字符串会关闭转义


机制。
raw 字符串不能以 \ 结尾。

本文档使用 书栈(BookStack.CN) 构建 - 17 -
04. 字符串

3.字符串块:以三重引号包围的字符串(引号为一对单引号或者一对双引号均可)。
三重引号之间的文本会被收集到一个单独的多行字符串中,并且将换行以 \n 代替。

4.字符串可以有以下操作:

加:两个字符串相加,返回一个新的字符串
乘:字符串与整数 N 相乘,返回一个新的字符串,该字符串是原字符串重复 N 次
len() :返回字符串长度
迭代:字符串对象是一个可迭代对象

5.字符串支持索引,其中 S[i] 获取的是偏移为 i 处的字符(偏移 i 从0开始,


小于等于 len(S)-1 )。

若偏移 i 大于 len(S)-1 则Python会抛出 IndexError 异常,


提示 string index out of range

你也可以提供一个负的偏移,其中 -1 为最后一个字符, -n 为 len(S)-n 处的字符。

6.字符串支持分片,语法为 S[m:n] ,它返回从 m 开始到 n (不包含 n )的一个新字符串。

未给出上界时,该分片默认上界为 len(S)

未给出下界时,该分片默认下界为 0
如果是 S[:] ,则返回字符串 S 的一个全新拷贝

如果增加步进参数,则语法为 S[m:n:k] ,它返回从 m 开始到 n (不包含 n )且每隔 k 个元素选取一次的


一个全新字符串,默认 k 为 1。

若 k 为正数则从左到右步进;若 k 为负数,则从右向左步进
S[::-1] 返回S的翻转字符串

本文档使用 书栈(BookStack.CN) 构建 - 18 -
04. 字符串

负步进时,默认的上下界极限有所不同。
上界极限可以为空或者为 len(S) 或者为 len(S)-1

下界极限必须为空,否则 0 号元素会被排除掉

7.可以通过 str() 函数与 repr() 函数将数字转成字符串。

str() 函数的效果类似 print() 的效果


repr() 函数产生的结果可以由解释器解释。 eval(repr(x)) 会返回 x 。

8.可以在单个字符与它对应的 ASCII 码值之间转换:

ord(char) :返回单个字符的 ASCII 值


chr(ascii_int) : 返回 ASCII 值对应的字符

9.字符串是不可变序列,因此不能原地修改一个字符串,只能够生成新的字符串并赋值给原变量。

10.字符串格式化表达式是基于C语言的printf格式化表达式。其格式为:

本文档使用 书栈(BookStack.CN) 构建 - 19 -
04. 字符串

1. "%d %s apples" % (3,'bad')

它返回一个新的字符串。

占位符有多种:
%s :字符串; %r :也是字符串,但用 repr() 得到的而不是 str() ;
%c :字符; %d :十进制整数; %i :整数; %e :浮点指数;
%f : 浮点十进制; %% :百分号 % , %g :自动转成浮点 %e 或者 %f

转换通用目标结构为: %[(key_name)][flags][width][.precision]type

key_name :用于从右边字典中取对应键的值(此时右边的是字典对象,而不是元组)
如: "%(n)%d %(x)%s" %{"n":3,"x":"apples"}

flags :如果为 - 则左对齐;如果为 + 则为正负号;如果为 0 :则补零


width : 指定位宽(包括小数点在内),至少为 width 字符宽度
precision :指定小数点后几位
width 和 precision 可以为 * ,表示它们从输入的下一项中取值
(即从元组中取得)

type 为类型,如 d , r , f , e 等

11.格式化字符串除了使用字符串格式化表达式之外,还可以通过字符串的 .format()

方法达到同样的效果。

.format() 方法支持位置参数、关键字参数、以及二者的混合。
位置参数: "{0},{1},{2}".format('abc','def','ghi')

关键字参数: "{k1},{k2},{k3}".format(k1='abc',k2='def',k3='ghi')

混合使用: "{0},{1},{k}".format('abc','def',k='ghi')

格式化字符串中可以指定对象的属性、字典键、序列的索引:
指定字典的键: "{0[a]}".format({'a':'value'}) ,注意这里的键 a 并没有引号包围
指定对象的属性: "{0.platform}".format(sys) ,也可以用关键字参数:
"{obj.platform}".format(obj=sys)

指定序列的索引: "{0[2]}".format("abcd") ,这里只能进行正索引值,且不能分片

本文档使用 书栈(BookStack.CN) 构建 - 20 -
04. 字符串

通用目标结构为: {fieldname!conversionflag:formatspec}

fieldname 为位置数字 0,1,2,… 或者为关键字,它后面可选地跟随


.name :则指定对象的属性
[index] :指定了索引
[key] :指定了字典的键
conversionflag 为转换标记,可以为:
r :在该值上调用一次 repr() 函数
s :在该值上调用一次 str() 函数
a :在该值上调用一次 ascii() 函数
formatspec 为格式,其结构为:
[[fill] align] [sign] [#] [0] [width] [.precision] [type]

fill 一般与 align 为 = 时配合


align 为对齐:
< :左对齐
> :右对齐
= :必须指定 fill (单个字符),此时用这个字符填充
^ :居中对齐
sign :为正负号标记
# :作用未知
0 :补0
width :位宽
.precision :精度
type :为类型,如 d , r , f , e 等,
但与格式化字符串表达式相比,多了 b (二进制格式输出)

某些值可以从 .format() 的参数中获取,如 "{0:+0{1}d}".format(128,8) ,

本文档使用 书栈(BookStack.CN) 构建 - 21 -
04. 字符串

其指定精度信息从 format() 的参数中取(参数8)

12.格式化单个值比较简单,可以有以下方法:

"%s" % 1.23

"%s" % (1.23,) ,这里 (1.23,) 是一个单元素元组,


而 (1.23) 是一个表达式
"{0}".format(1.23)

13.由于Python内部会暂存并重复使用短字符串来进行优化,因此该短字符串在内存中只有一份。

14.浮点数格式化时,采用 %s 说明符与 %f 说明符,其结果不同:

因为按照 %f 格式化输出时,浮点数有精度和位宽的设定(虽然这里没有显式指定,但是它们有默认值)。
而 %s 格式化输出时,首先调用了 str() 函数,然后再进行输出。

本文档使用 书栈(BookStack.CN) 构建 - 22 -
05. 列表

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

列表
1.列表可以包含任何种类的对象。它是本身是一种可变对象,支持原地修改。

不可变对象有元组、字符串、 frozenset

2.列表的基本操作有:

list1+list2 :列表相加合并生成新的列表,
并不会修改 list1 和 list2 的值
list1*5 : 列表乘以数字生成重复的新列表,
并不会修改 list1 的值

item in list1 : 返回元素 item 是否在列表 list1 中

用于迭代,并不会修改 list1 的值:

1. for item in list1:


2. pass

列表解析: [item*4 for item in iter_obj]

list() 函数: list(iter_obj) 生成新列表

本文档使用 书栈(BookStack.CN) 构建 - 23 -
05. 列表

索引和分片:

list1[index] :索引,获取指定偏移处的对象,
并不会修改 list1 的值
list1[index1:index2] :分片,返回一个新列表,
其元素值与旧列表对应片段处元素值相等,
并不会修改 list1 的值
当索引位于赋值左侧时,则是索引赋值。这会改变列表指定项的内容。修改 list1 的值

当分片位于赋值左侧时,则是分片赋值。这会改变列表指定片段的内容。
修改 list1 的值

被赋值的片断长度不一定要与赋值的片断长度相等

3.列表的方法调用有:

.append(val) 方法:在列表之后追加元素,原地修改
.sort(key=None, reverse=False) 方法:对列表进行排序,
原地修改
key 为一个排序方法
reverse 为是否逆序
.reverse() 方法:原地翻转列表,原地修改
.extend(iter_obj) 方法:在列表末端插入多个元素,原地修改
.pop() 方法:删除末尾元素并返回该元素,原地修改

本文档使用 书栈(BookStack.CN) 构建 - 24 -
05. 列表

.pop(index) 方法:删除指定位置元素并返回该元素,原地修改
del list1[0] 函数:删除指定位置处元素,原地修改
与 .pop() 的区别在于 del() 函数并不返回该元素

del list1[index1:index2] 函数:删除指定片断处元素, 原地修改


.remove(val) :通过值删除元素,若有多个值,则只删除第一个遇到的值 原地修改
.insert(index,val) :在指定位置插入元素,原地修改

.index(val) :返回指定元素的位置,若有多个值,则只返回第一个遇到的值所在位置

4.列表相关的内置函数:

sorted(list1,key=None,reverse=False) :排序列表并返回新列表, 非原地修改


reversed(list1) :返回迭代器,该迭代器迭代结果就是列表的逆序

本文档使用 书栈(BookStack.CN) 构建 - 25 -
05. 列表

5.列表不允许引用超出列表末尾的索引。

6.列表解析表达式:通过对可迭代对象迭代生成的项运行一个表达式的方式创建一个新列表。如:
[c*2 for c in "apple"]

7. list(iter_obj) 可以通过可迭代对象 iter_obj 生成一个新列表

8.假设 L=[1,2] ,注意下面的用法的区别:

X=L*2 :列表乘以整数,生成新列表 [1,2,1,2]

Y=[L]*2 :也是列表乘以整数,但是被乘列表是 [[1,2],] ,


生成的新列表是 [[1,2],[1,2],]

列表元素为可变对象时注意,这里改变 L 会影响 Y ,因为 Y 持有 L 的引用

9.如果列表的元素是可变对象的,则对该可变对象的修改会影响到列表。

本文档使用 书栈(BookStack.CN) 构建 - 26 -
06. 元组

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

元组
1.元组是由圆括号包围的一系列项,它由任意对象元素组成。它本身是不可变的,
即不可以原地修改。

set 与 tuple 相反, set 是本身可变,但是其元素不可变。

元组的元素可以为可变对象,也可以为不可变对象。
元组是有序的序列
元组中的元素可以通过偏移访问,支持索引和分片,但是不支持索引赋值和分片赋值
通过索引访问返回一个值
通过分片访问返回一个新的元组
() 为空元祖, (0,) 为单元素元组(注意逗号 , ), (0) 为括号表达式
tuple(iter_obj) 函数从一个可迭代对象生成元组
len(tuple1) :该函数获取元组长度
item in tuple1 :判断元素值 item 是否在元组中

元组是一个可迭代对象,它支持迭代:

1. for item in tuple1:

2. pass

.index(val) 方法:在元组中搜索 val 值所在位置


.count(val) 方法:在元组中累计 val 值出现的次数

本文档使用 书栈(BookStack.CN) 构建 - 27 -
06. 元组

2.在不会引起语法冲突的情况下,Python支持忽略元组中的圆括号。如 x=10,20 等价于 x=(10,20)

本文档使用 书栈(BookStack.CN) 构建 - 28 -
07. 通用序列

通用序列
1.Python的通用序列包括:字符串、列表、元组。

字符串:不可变对象,元素只能是字符
列表:可变对象,元素类型不限
元组:不可变对象,元素类型不限
这里的 可变 指的是能否修改对象,如增加序列元素,修改序列元素等等

2.Python通用序列操作有:

索引操作: seq[index] ,返回序列在该位置处的值,有越界检查


分片操作: seq[index1:index2] :返回一个新的序列,有越界检查
序列加法: seq1+seq2 :返回一个新序列,包含 seq1 、 seq2 的拷贝
序列乘法: seq*N :返回一个新序列,包含 seq 的 N 份拷贝

3. set 不是序列,它是可变对象,但是元素只能是不可变类型。
字典也不是序列,它是可变对象,其元素的值是不限类型,但是键必须是不可变类型。

4.三种解析表达式:

列表解析表达式: [c*4 for c in 'abcd'] 生成一个新列表对象,


结果为: ['aaaa','bbbb','cccc','dddd']

set 解析表达式: {c*4 for c in 'abcd'} 生成一个新 set 对象,


结果为: {'aaaa','bbbb','cccc','dddd'} (打印顺序不确定)
字典解析表达式: {c:c*4 for c in 'abcd'} 生成一个新列表对象,
结果为: {'a':'aaaa','b':'bbbb','c':'cccc','d':'dddd'} (打印顺序不确定)

注意:并没有元组解析表达式。 (c*4 for c in 'abcd') 返回的是一个生成器对象。

本文档使用 书栈(BookStack.CN) 构建 - 29 -
07. 通用序列

5.Python 3.0 中, bytearray 字节串类型是可变的

6.序列的拷贝:

分片表达式能复制序列, 只能进行顶层拷贝
字典的 .copy() 方法能复制字典, 只能进行顶层拷贝
通过 list() 、 tuple() 等方法
通过 copy 标准库模块,可以进行深层拷贝

7.比较操作时,Python能够自动遍历嵌套的对象,从左到右递归比较,要多深有多深。过充中首次发现的差异将决定
比较的结果。

数字通过相对大小比较
字符串从左到右每个字符按照字符的字典序比较
列表和元组从左到右每部分内容都比较

字典通过排序后的 (键,值) 列表比较

Python 3.0 中的字典只能进行相等、不等比较

数字与其他类型比较在 Python 3.0 中是错误的

本文档使用 书栈(BookStack.CN) 构建 - 30 -
07. 通用序列

8.当一个复合对象包含指向自身的引用时(如 L.append(L) ),称为循环对象。当Python在对象中检测到循环时,


会打印成 [...] 而不会陷入无限循环。

本文档使用 书栈(BookStack.CN) 构建 - 31 -
08. 字典

字典
1.字典是一些 “键 —- 值” 对的无序集和。它通过键来索引。

字典是可变对象,它的元素的值的类型不限,它的元素的键类型是不可变类型

意味着键类型不能是列表、 set 、,字典

字典的常量表达式: {'a':3,'b':4} ,空字典的常量表达式为 {}

dict() 函数可以从关键字参数生成字典: dict(a=3,b=4) 生成字典 {'a':3,'b':4}

你可以通过 zip() 函数生成关键字参数: dict(zip(['a','b'],[3,4]))

生成字典 {'a':3,'b':4}

你也可以用字典的 .fromkeys() 类方法生成字典:

dict.fromkeys(['a','b']) 生成字典 {'a':None,'b':None}

dict.fromkeys(['a','b'],3) 生成字典 {'a':3,'b':3}

字典索引: d[key] 。字典索引返回对应的值

键测试: key in d 。测试指定键是否存在字典中


字典的迭代:
d.keys() :返回一个dict_keys对象,它是一个可迭代对象,迭代时返回键序列
d.values() :返回一个dict_values对象,它是一个可迭代对象,迭代时返回值序列
d.items() :返回一个dict_items对象,它是一个可迭代对象,
迭代时返回元组 (键,值) 的序列
字典迭代在Python3中返回可迭代对象,在Python2.7中均返回列表。

1. 因此在Python3中如果想得到列表,必须将返回值传入`list()`函数中得到列表

字典的拷贝: d.copy() 。只是字典的浅拷贝

获取键的值:通过 d.get(key,default_value) 。返回键对应的值, 若键不存在则返回


default_value

对于 d[key] 返回对应的值,如果 key 不存在则抛出 KeyError 异常

本文档使用 书栈(BookStack.CN) 构建 - 32 -
08. 字典

字典的操作:
d1.update(d2) :合并两个字典,原地修改 d1 字典
d.pop(key) : 从字典中删除 key 并返回该元素的值
del d[key] :从字典中删除 key 但是不返回该元素的值
d[key]=value :原地的添加/修改字典。当向不存在的键赋值时,相当于添加新元素
获取字典中元素数量: len(d) 。它也等于键列表/值列表的长度

字典本身也是一个可迭代对象,它的迭代方法为:

1. for key in d:
2. pass

它在列表的键上迭代,也等价于

1. for key in d.keys()

本文档使用 书栈(BookStack.CN) 构建 - 33 -
08. 字典

2. pass

2.Python3中字典的变化:

d.keys() 、 d.values() 、 d.items() 返回的是可迭代对象,他们称为视图对象,


而不是列表。修改字典,则这些视图对象会相应变化
支持字典解析表达式来生成字典,如 {k:v for k,v in zip(['a','b','c'],[1,2,3])} ,
{k:v for k,v in [('a',1),('b',2),('c',3)]}

取消了Python2中的 has_key(key) 方法,而用 key in d 表达式取代


Python3中,只有相等不等测试有效,字典的大小比较无效

3.Python3中字典是一个迭代器对象,其迭代结果是返回键序列

本文档使用 书栈(BookStack.CN) 构建 - 34 -
09. 文件

文件
1.内置的 open() 函数会创建一个Python文件对象,可以作为计算机上的一个文件的引用。

2.打开文件: outfile=open(r'C:\filename','w') :

第一个参数 r'C:\filename' 为文件名字符串

第二个参数 'w' 为打开模式字符串。有以下三种:

'r' :读打开(默认行为)
'w' :写打开

'a' :追加写打开

第二个参数也可以添加 'b' 表示二进制处理。二进制处理中,换行符未转换,


同时Python3中 Unicode编码被关闭
第二个参数也可以添加 '+' 表示读写同时作用

还有第三个参数可选,用于控制输出缓冲. '0' 表示无缓冲(只能在二进制模式中使用无缓冲)。

3.文件对象的方法:

文件读入:
.read() :读取接下来的整个文件到单个字符串
.read(n) :读取接下来的 n 个字节到一个字符串
.readline() :读取下一行到一个字符串(包括行末的换行符)
.readlines() :按行读取接下来的整个文件到字符串列表,每个字符串一行

写入文件:
.write(str) :写入字符串到文件(并不会自动添加换行符以及其他任何字符, str 是啥就写啥),
返回写入的字符数
.writelines(strlist) :将字符串列表内所有字符串依次写入文件(并不会自动添加换行符以及其他任何
字符)

本文档使用 书栈(BookStack.CN) 构建 - 35 -
09. 文件

关闭文件: .close() 方法。


刷新输出缓冲区: .flush() 方法。通常关闭文件会将输出缓冲区内容写入到文件中;但用 .flush() 方法不必
关闭文件。
定位文件: .seek(N) 方法,将文件偏移修改到字节 N 处以便进行下一次操作

文件迭代:文件对象也是一个可迭代对象,每一次迭代返回一行,对于大型文件一次性读取非常耗内存和性能:

1. for line in open('data'):#每次循环迭代时,自动读取并返回一行


2. #use line
3. pass

读写文本文件时,默认的编码是 utf-8 ,你也可以使用指定的编码: open(r'filename',encoding='latin-1')

读二进制文件用 openopen(r'filename','rb')

4.从文本文件中读取文字行的最佳方式是用文件迭代器,不要直接读取文件

5.当文件对象被自动收回时,Python会自动关闭该文件,这意味着不一定要手动调用 .close() 方法

6.默认的写操作总是缓冲的。当文件关闭或者 .flush() 方法调用时,缓冲的输出数据会写入硬盘。

7.文件的空行是含有换行符的字符串,而不是空字符串。因此如果读入操作返回空字符串,则表示已经到文件末尾
了。

8.Python3中,文本文件将内容表示为常规的 str 字符串,自动执行Unicode编码和解码,并且默认执行行末转


换。而二进制文件将内容表示为一个特殊的 bytes 字节串类型,且运行程序不修改地访问文件内容。

不能以文本格式打开一个二进制数据文件,会乱码

本文档使用 书栈(BookStack.CN) 构建 - 36 -
09. 文件

9.可以在文件中存储并解析Python对象。由于文件数据在脚本中是字符串,文件对象的 .write() 方法不会自动地


将Python对象转成字符串,因此需要手工转换。

可以用格式化字符串方法或者 str() 方法将Python对象转成字符串


将字符串转换成Python对象可以用 eval() 方法
或者直接使用 pickle 模块自动存储和解析Python对象:
存储: pickle.dump(obj,file) ,其中 obj 是要存储的Python对象, file 文件对象
(用二进制写打开)
加载: obj=pickle.load(file) ,其中 file 文件对象(用二进制读打开)

10. struct 模块能够打包/解包二进制数据

打包: data=struct.pack('>i4sh',7,b'abcd',8) 。其中第一个参数为格式说明字符串,后面的参数为待打包的数


据。格式说明字符串为:
> 为说明符
i 说明第一个待打包的数据为整数
4s 说明第二个待打包的数据为4个字节的字节串
h 说明第三个待打包的数据为16进制整数
解包: val=struct.unpack('>i4sh ,data)`。其中第一个参数为格式说明符,第二个参数为已经打包的二进制数
据,返回一个元组。

11. sys 模块中有几个预先打开的文件对象:

sys.stdout 对象:标准输出对象
sys.stdin 对象:标准输入对象

本文档使用 书栈(BookStack.CN) 构建 - 37 -
09. 文件

sys.stderr 对象:标准错误输出对象

12. os 模块中的文件描述符对象支持文件锁定之类的低级工具

13. socket 、 pipe 、 FIFO 文件对象可以用于网络通信与进行同步

本文档使用 书栈(BookStack.CN) 构建 - 38 -
10. 语句和语法

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

语句和语法
1.所有Python的复合语句,都是以冒号 : 结尾,下一行缩进开始进入代码块。同一个级别代码块的缩进形式相同

2.测试( if | while )中的一对圆括号 () 是可选的

3.可以省略行尾的分号 ;

4.代码块的范围由缩进来决定。同一个块中,所有语句向右缩进相同的距离(块内语句垂直左对齐)。

缩进可以用空格或者制表符。不应该在同一段代码中混合使用制表符和空格
若缩进出现不一致,则会导致语法错误

5.若将多行语句列入一行,则必须用分号隔离 ;

6.任何在括号中的多行语句都视为一行。括号包括圆括号 () ,中括号 [] ,大括号 {} 。也可以用反斜


线 \ 转义换行符来跨多行

7.当复合语句不包含任何复合从句时,复合语句的主体可以出现在Python首行冒号之后:
if x>y : print(x)

8.变量名由:下划线或字母开头,后面接任意字母、数字、下划线

以单下划线开头的变量名不会被 from module import * 语句导入,如变量名 _x

前后双下划线的变量名是系统预定义的,对解析器有着特殊的意义,如变量名 __x__

仅前面有双下划线的变量名视为类的本地变量,如变量名 __x

9.表达式可以作为语句,但是其结果不会存储。因此只有当表达式工作产生副作用时,这种用法才有意义。

表达式可以作为语句,但是语句不能作为表达式。如 Python中不支持(C和C++可以,因为在C/C++中赋值表
达式产生左值):

1. if( a=file.read() ):

2. pass

列表的原地修改表达式返回 None 对象,因此以下的意图不正确: L=L.append('a') ,会导致 L 为 None 。

10.Python的语句是逐条运行的,除非遇上控制流语句。

块|语句的边界是自动检测的。缩进定义了块边界,换行定义了语句边界
首行+ : +缩进语句定义了复合语句
空白行、空格、注释通常被解释器忽略
文档字符串会被解释器忽略,但是会保存它并由工具显示

11.Python顶层程序代码必须不能有缩进。缩进发生在复合语句的字块中。

本文档使用 书栈(BookStack.CN) 构建 - 39 -
11. 赋值语句

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

赋值语句
1.赋值的左侧可以为变量名或者对象元素,右侧为任何表达式

赋值总是建立对象的引用值,而不是复制对象
变量名会在首次被赋值时创建。此后,每当这个变量名出现在表达式中时,会被它引用的对象所替代
变量名必须先赋值后引用,否则报错
模块导入、函数定义、类定义、 for 循环、函数参数传递 等过程都会触发隐式赋值,原理、规则同显式赋值

2.赋值语句的形式:

基本形式: x='abcd'

元组赋值: a,b="ab","cd" ,按照位置一一对应赋值


列表赋值: [a,b]=["ab","cd"] ,按照位置一一对应赋值
扩展的序列解包赋值: a,*b="abcd" ,结果是 b 等于 ['b','c','d']

多目标赋值: a=b="abcd"
注意此时 a 和 b 都引用同一个对象。如果这个对象是个可变对象,则使用 a 或者 b

1. 对它进行原地修改可能导致陷阱

增强赋值: a+='ef' ,等价于 a=a+ ef`


若 a 指向的是可变对象,则 += 很有可能是原地操作
所有的二元表达式运算符均有增强赋值语句

3.Python3中,元组和列表赋值统一化为序列赋值: a,b=['ab','cd'] ,左侧为任何类型的变量名序列,右侧为任何


类型的值序列,只需要变量名序列和值序列长度相等。赋值时根据位置一一对应赋值。

若变量名序列和值序列长度不等,则抛出 ValueError 异常
支持嵌套的赋值序列,Python会自动根据实际情况分解成其组成部分,然后递归赋值。要求左侧变量名序列的
嵌套形状必须符合右侧值序列的嵌套形状。

4.扩展的序列解包赋值:收集右侧值序列中未赋值的项为一个列表,将该列表赋值给带星号 * 的变量

左边的变量名序列长度不需要与值序列的长度相等,其中只能有一个变量名带星号 *

若带星号 * 变量名只匹配一项,则也是产生一个列表,列表中只有一个元素,
如 a,*b="12" , b 为 [2]

若带星号 * 变量名没有匹配项,则也是产生空列表,如 a,*b="1" , b 为 []

带星号 * 的变量名可以出现在变量名序列中的任何位置如 *a,b="1234" , a 为 [1,2,3]

匹配过程优先考虑不带星号的变量名,剩下的才匹配带星号的变量名
以下情况会引发错误:

本文档使用 书栈(BookStack.CN) 构建 - 40 -
11. 赋值语句

左侧变量名序列有两个星号,如 *a,*b="abcd"

左侧变量名序列无星号但是左右长度不匹配,如 a,b="abcd"

左侧变量名序列星号的名称不在序列中,如 *a='abcd'

可以用手动分片来模拟扩展赋值行为

5.增强赋值的优点:

X+=Y 中, X 可以是复杂的对象表达式,只需要求值一次。而 X=X+Y 中,要对 X 求值两次


对支持原地修改的对象而言,增强形式的赋值会自动执行原地修改的预算:
L=L+[1,2] 会生成新对象
L+[1,2] 会执行原地修改

本文档使用 书栈(BookStack.CN) 构建 - 41 -
12. 打印

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

打印
1.打印 print 类似于文件 .write() 方法,它将默认地把对象打印到 stdout 流中。它会自动添加一些自动化的格
式。

和文件的 .write() 方法不同的是, print 不需要将对象转换为字符串

2.在Python3中, print 是一个内置函数,用关键字参数表示特定模式,其语法格式为:


print(obj1,obj2,sep=' ',end='\n',file=sys.stdout) ,返回值为 None

参数意义依次为:
待打印对象作为位置参数依次给出
sep 关键字参数指定分隔符,默认为空格
end 关键字参数指定结尾字符串,默认为换行符
file 指定输出位置,默认为标准输出文件,它必须是一个写打开的文件对象
每个被打印的对象依次自动通过内置的 str() 函数取得其文本表示
当没有打印对象时, print() 会把一个换行符(或者由 end 指定的其他字符串)打印到标准输出流中(或者
由 file 指定的文件中)
关键字参数可以以任何顺序出现,但必须在位置参数之后

如果想指定对齐或者位宽,则可以先构建格式化表达式来生成字符串,然后在 print 这个字符串

Python2中, print 是语句,有自己的特定语法: print x,y ,如果想指定结尾字符串(默认为换行),则用 print x,y,


'\t'

3. print 实际上是向文件对象中写文本字符串,因此对于字符串常量的一对引号实际上是不输出的,它只是输出字
符串的内容。而交互式命令中,为了显示指定字符串,输出中带有一对引号。

4.你也可以用 sys.stdout.write(str(x)+' '+str(y)+'\n') 代替 print(x,y)

5. file 关键字指定的对象可以是文件对象,也可以是拥有 .write() 方法的其他对象

6.指定了默认的 file 关键字参数时,也可以输出重定向,如

1. temp=sys.stdout #保存旧值

2. sys.stdout=open('test.txt','a') #重新对stdout赋值,该文件对象必须写打开

3. print(obj1,obj2)
4. sys.stdout.close() #此句可以不要,此时文件对象自动回收,文件自动关闭
5. sys.stdout=temp #恢复旧值

7.在Python3中,可以通过 from __future __import print_function 使用Python3中的 print() 函数

本文档使用 书栈(BookStack.CN) 构建 - 42 -
12. 打印

8.Python3的 input() 直接把输入的文本作为一个字符串返回,不会求值


(输入什么,字符串中就是什么)。

Python2中的 input() 会对字符串求职,就像他们是输入到一个脚本的程序代码一样

本文档使用 书栈(BookStack.CN) 构建 - 43 -
13. if语句

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

if 语句
1. if 语句的的通用格式:

1. if test_expr1: #必选
2. statement1#必选
3. elif test_expr2: #可选
4. statement2

5. else: #可选
6. statement3

注意 if 、 elif 、 else 的缩进一致


除了开头的 if 以及关联的子句外, elif 、 else 均可选

2.Python中多路分支必须写成一系列的 if/elif 测试,因为Python中没有 switch-case 语句

字典也可以执行多路分支的逻辑,如:

1. mydict={'a':func1,'b':func2}
2. choice='a'

3. print(mydict[choice])

3.Python中的真值测试:

任何非0数字或者非空对象为 True ,数字0、空对象(如空列表,空字典、空元组、空 set 、空字符


串)、 None 对象为 False

比较、相等测试会递归地应用在嵌套的数据结构中,他们返回 True 或 False

布尔 and 和 or 运算符会返回真或假的操作对象,而不是 True 或 Flase ,并且它们是短路计算

and :从左到右依次对操作对象求值,停在第一个为假的对象上并返回它,或者当前面所有操作对象为
真时返回最后一个操作对象
or :从左到右依次对操作对象求值,停在第一个为真的对象上并返回它,或者当前面所有操作对象为假
时返回最后一个操作对象

4.Python支持 if|else 三元表达式: Y if X else Z :


当 X 为真时,表达式的值为 Y ;当 X 为假时,表达式的值为 Z 。
注意这里为短路计算,并不会同时对 Y 和 Z 求值。

三元表达式 X? Y:Z 也能得到同样的效果


还有一种模拟方法: [Z,Y][bool(X)] 。但是它会同时对 Z 、 Y 求值,可能会有副作用。
bool(X) 将 X 转换成对应的1或者0

本文档使用 书栈(BookStack.CN) 构建 - 44 -
14. while、for循环

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

while 、 for 循环
1. while 语句格式:

1. while test_expr:
2. statement1
3. else:#可选
4. statement2

while 和 else 缩进必须一致。


else 可选。 else 子句在控制权离开循环且未碰到 break 语句时执行。即在正常离开循环时执行( break 是
非正常离开循环)

在 while 子句中可以使用下列语句:

break :跳出最近所在的循环到循环外部
continute :跳过本次循环后续部分,直接掉转到下一轮循环起始处
pass :占位符,什么都不做
在Python3中,允许在使用表达式的地方使用 ... 代表,
这是 pass 语句的一种替代方案。它表示代码随后填充,是未确定的内容

2. for 语句格式:

1. for target_var in iter_obj:


2. statement1

3. else:#可选
4. statement2

for 和 else 缩进必须一致。


else 可选。 else 子句在控制权离开循环且未碰到 break 语句时执行。即在正常离开循环时执行( break 是
非正常离开循环)
在 for 子句中可以使用 break 、 continute 、 pass 语句

target_var 是赋值目标, iter_obj 是任何可迭代对象。每一轮迭代时将迭代获得的值赋值


给 target_var ,然后执行 statement1

任何赋值目标在语法上均有效,即使是嵌套的结构也能自动解包:

1. for ((a,b),c) in [((1,2),3),((4,5),6)]:#自动解包

2. print(a,b,c)

本文档使用 书栈(BookStack.CN) 构建 - 45 -
14. while、for循环

当然你也可以手动解包:

1. for both in [((1,2),3),((4,5),6)]:#手动解包


2. ((a,b),c)=both
3. print(a,b,c)

3. for 扫描文件时,直接使用文件对象迭代,每次迭代时读取一行,执行速度快,占用内存少:

1. for line in open('test.txt'):


2. print(line)

4. for 语句通常比对应的 while 语句执行速度快

5.Python3中, range() 返回一个迭代器对象。用法为: range(0,10,2) ,其中 0 为起始数, 10 为终止数


(不包含), 2 为步长。默认步长为1,起始为0.

Python2中, range() 返回一个列表对象

要得到列表,用 list(range(0,10,2))

通常 range() 用于 for 循环:

1. S="abcdefg"
2. for i in range(0,len(S),2):
3. print(S[i],end=" ")

它也等价于下列循环:

1. S="abcdefg"

2. for c in S[::2]
3. print(c,end=" ")

用range()优点是它并未复制字符串

当我们遍历列表且对其进行修改时,要用到 range() 和 len() 组合。直接用 for 遍历列表不能修改列表,因


为 for x in L: 遍历的是列表元素,不是列表中元素的位置。

6.Python3中, zip() 函数返回一个迭代器对象。

Python2中, zip() 返回一个元组对的列表

list(zip(L1,L2)) 创建一个列表,列表元素为元组对,元组对的第一个元素来自于 L1 ,第二个元素来自


于 L2 ,列表长度为 L1 与 L2 的最小值
zip() 可以有两个以上的参数,且这些参数可以是任意的可迭代对象

本文档使用 书栈(BookStack.CN) 构建 - 46 -
14. while、for循环

可以在循环中用自动列表解包:

1. for x,y,z in zip(iter_obj1,iter_obj2,iter_obj3):

2. pass

7.Python3中, map() 函数生成一个可迭代对象,用法为: map(func,iter_obj) 。每一次迭代则在迭代得到的元素


上应用函数 func 。

Python2中, map() 执行的是另一种语意

8. enumerate() 函数生成一个可迭代对象,用法为: enumerate(iter_obj) 。每一次迭代生成一


个 (index,value) 元组, index 表示迭代次数,从0开始计数, value 代表迭代获得的元素值。

本文档使用 书栈(BookStack.CN) 构建 - 47 -
15. 迭代器和生成器

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

迭代器和生成器
1.可迭代对象:在逻辑上它保存了一个序列,在迭代环境中依次返回序列中的一个元素值。

可迭代对象不一定是序列,但是序列一定是可迭代对象

2.迭代协议: .__next__() 方法。

任何对象只要实现了迭代协议,则它就是一个迭代器对象
迭代器对象调用 .__next__() 方法,会得到下一个迭代结果
在一系列迭代之后到达迭代器尾部,若再次调用 .__next__() 方法,则会触发 StopIteration 异常
迭代器在Python中是用C语言的速度运行的,因此速度最快

3.Python3提供了一个内置的 next() 函数,它自动调用迭代器的 .__next__() 方法。即给定一个迭代器对


象 x , next(x) 等同于 x.__next__()

4.内置的 iter() 函数用于从序列、字典、 set 以及其他可迭代对象中获取迭代器。

对任何迭代器对象 iterator ,调用 iter(iterator) 返回它本身


迭代器对象实现了迭代协议
文件对象本身是一个迭代器对象。即文件对象实现了迭代协议,因此打开多个文件会返回同一个文件对象

列表、元组、字典、 set 、字符串等不适迭代器对象,他们没有实现迭代协议。因此每次调用 iter() 均返


回一个新迭代器对象。他们支持安装多个迭代器,每个迭代器状态不同

在原地修改列表、 set 、字典时,会实时反映到它们的迭代器上

5.文件迭代器:文件对象本身是一个迭代器(这里文件对象要是读打开)。它的 .__next__() 方法每次调用时,返回


文件中的下一行。当到达文件末尾时, .__next__() 方法会引发 StopIteration 异常。

.readline() 方法在到达文件末尾时返回空字符串

6.字典的迭代器:字典的迭代器在迭代环境中,每次迭代返回的是一个键。

7.扩展的列表解析表达式:
[ x+y for x in 'abc' if x!='a' for y in 'lmn' if y!='l']

本文档使用 书栈(BookStack.CN) 构建 - 48 -
15. 迭代器和生成器

其通用结构为:

1. [ expression for target1 in iterable1 [if condition1]

2. for target2 in iterable2 [if condition2]


3. ....]

我们总是可以用 for 循环手动构建列表解析表达式的结果,但是列表解析表达式性能更好

8. for 循环、列表解析、 in 成员关系测试、 map() 内置函数、 sorted() 内置函数、 zip() 内置函数等都是


用迭代协议来完成工作

9.常见的迭代函数:

map(func,iterable) :它将函数 func 应用于传入的迭代器的每个迭代返回元素,返回一个新的迭代器,函数


执行结果作为新迭代器的迭代值

map() 可以用于多个可迭代对象: map(func,[1,2,3],[2,3,4]) ,其中 func(first,second) 的两个参数分别从两个可迭


代对象中获取,函数结果作为新迭代器的迭代值

zip(iterable1,iterable2,...) :它组合可迭代对象 iterable1 、 iterable2 、 ... 中的各项,返回一个新的迭


代器。新迭代器长度由 iterable1 、 iterable2 、 ... 最短的那个决定。

enumerate(iterable,start) :返回一个迭代器对象,它迭代结果是每次迭代返回一个 (index,value) 元组

filter(func,iterable) :返回一个迭代器对象,它的迭代结果得到 iterable 中部分元素,其中这些元素使


得 func() 函数返回为真

reduce(func,iterable,initial) :对 iterable 中每一项成对地运行 func ,返回最终值

reduce 函数位于 functools 包内

sorted(iterable,key=None,reverse=False) :排序并返回排好的新列表

sum(iterable,start) :返回可迭代对象中的累加值

any(iterable) :只要可迭代对象 iterable 迭代返回的某个元素为真则返回 True

本文档使用 书栈(BookStack.CN) 构建 - 49 -
15. 迭代器和生成器

all(iterable) :只有可迭代对象 iterable 迭代返回的所有元素为真则返回 True

max(iterable,key=func) :返回最大元素。若指定 func ,则返回是 func(num) 最大的那个元素

min(iterable,key=func) :返回最小元素。若指定 func ,则返回是 func(num) 最小的那个元素

10. set 解析、字典解析支持列表解析的扩展语法

11.Python3中, range 对象不支持 .__next__() ,因此它本身不是迭代器,而 map 、 zip 、 filter 对象都


是迭代器。

range 不直接生成列表的优点是节约内存空间
由于 map 、 zip 、 filter 对象都是迭代器,因此不支持在它们身上安装多个活跃的迭代器。对他们调
用 iter() 其实返回的是它们本身。

12.字典的视图:键视图、值视图、字典视图都没有 .__next__() 方法,因此他们都不是迭代器

13.通常列表解析执行速度最快, map() 速度次之, for 循环速度最慢。前两者以C语言速度执行、后者在


Python虚拟机上执行

14.生成器函数:编写为常规的 def 语句,但是用 yield 语句一次返回一个结果。每次使用生成器函数时会继续上


一轮的状态。

生成器函数会保存上次执行的状态

定义生成器函数的语法为:

1. def genFunc(num):

2. for i in range(num):
3. yield i**2

生成器函数执行时,得到一个生成器对象,它 yield 一个值,而不是返回一个值。


生成器对象自动实现迭代协议,它有一个 .__next__() 方法
对生成器对象调用 .__next__() 方法会继续生成器函数的运行到下一个 yield

结果或引发一个 StopIteration 异常
yield 语句会挂起生成器函数并向调用者发送一个值。当下一轮继续时,函数会在上一个 yield 表达式返回后
继续执行,其本地变量根据上一轮保持的状态继续使用
生成器函数运行代码随时间产生一系列的值,而不是一次性计算它们。这会节约内存并允许计算时间分散。

本文档使用 书栈(BookStack.CN) 构建 - 50 -
15. 迭代器和生成器

15. for 循环(及其它迭代环境)通过重复调用 .__next__() 方法直到捕获一个异常。若一个不支持迭代协议的对


象想用工作在这种环境中, for 循环会尝试使用索引协议迭代。

16.生成器对象有一个 .send(arg) 方法。该方法会将 arg 参数发送给生成器作为 yield 表达式的返回值,同时生


成器会触发生成动作(相当于调用了一次 .__next__() 方法。

yield 表达式的返回值和生成值是不同的。
返回值是用于生成器函数内部, yield 表达式默认返回值为 None ;
而生成值是用于生成器函数外部的迭代返回。

生成器对象必须先启动。启动意味着它第一次运行到 yield 之前挂起

要想启动生成器,可以直接使用 next(generatorable) 函数,也可以使用 generatorable.send(None) 方法,或者


generatorable.__next__() 方法
next(generatorable) 函数相当于使用 generatorable.send(None) 方法

generatorable.send(None) 方法会在传递 yield 表达式的值(默认为 None 返回值),下一轮迭代


从 yield 表达式返回开始

每一轮挂起时, yield 表达式 yield 一个数,但是并没有返回(挂起了该 yield 表达式)

17.生成器表达式:类似于列表解析,但是它是在圆括号中的,而不是方括号中的。

生成器表达式返回的是一个生成器对象
列表解析的结果等同于 list() 内参数为一个生成器表达式
当生成器表达式在其他括号之内时,它本身的圆括号可以不写
同样的迭代可以用生成器函数或者一个生成器表达式
生成器函数可以包含更多的逻辑
生成器表达式更简洁,没有函数调用产生生成器的过程
生成器函数与生成器表达式支持自动迭代与手动迭代
生成器对象本身是迭代器,因此只支持一个活跃的迭代器。

18.生成器函数可以有 return ,它可以出现在函数内任何地方。生成器函数内遇到 return 则触


发 StopIteration 异常,同时 return 的值作为异常说明

19.可以调用生成器对象的 .close() 方法强制关闭它。这样再次给它 send() 任何信息,都会抛


出 StopIteration 异常,表明没有什么可以生成的了

20. yield from :从 PEP 380 引入的新特性。

yield from 可以将一个大的生成器切分成小生成器:

1. def generator(): #该生成器yield数字[0~19]

本文档使用 书栈(BookStack.CN) 构建 - 51 -
15. 迭代器和生成器

2. for i in range(10):
3. yield i

4. for j in range(10,20):
5. yield j

如果你想切分成两个迭代器,可以这么做:

1. def generator2():
2. for i in range(10):
3. yield i

4. def generator3():
5. for j in range(10):
6. yield j
7. def generator():

8. for i in generator2():
9. yield i
10. for j in generator3():
11. yield j

引入 yield from 之后你可以这么做:

1. def generator2():

2. for i in range(10):
3. yield i

4. def generator3():
5. for j in range(10):

6. yield j
7. def generator():

8. yield from generator2()


9. yield from generator3()

yield from 能实现代理生成器:

1. def generator():

2. inner_gen=generator2()
3. yield from inner_gen #为了便于说明,这里分两行写

4. gen=generator()

对 inner_gen 迭代产生的每个值都直接作为 gen yield值


所有 gen.send(val) 发送到 gen 的值 val 都会被直接传递给 inner_gen 。
inner_gen 抛出异常:
如果 inner_gen 产生了 StopIteration 异常,
则 gen 会继续执行 yield from 之后的语句
如果对 inner_gen 产生了非 StopIteration 异常,则传导至 gen 中,
导致 gen 在执行 yield from 的时候抛出异常
gen 抛出异常:
如果 gen 产生了除 GeneratorExit 以外的异常,则该异常直接 throw 到 inner_gen 中
如果 gen 产生了 GeneratorExit 异常,或者 gen 的 .close() 方法被调用,

本文档使用 书栈(BookStack.CN) 构建 - 52 -
15. 迭代器和生成器

则 inner_gen 的 .close() 方法被调用。


gen 中 yield from 表达式求职结果是 inner_gen 迭代结束时抛出的 StopIteration 异常的第一个参数
inner_gen 中的 return xxx 语句实际上会抛出一个 StopIteration(xxx) 异常,
所以 inner_gen 中的 return 值会成为 gen 中的 yield from 表达式的返回值。

本文档使用 书栈(BookStack.CN) 构建 - 53 -
16. 文档

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

文档
1. # 是注释符,它之后的文字到行尾均为注释。Python解释器会忽略 # 之后的所有字符

2. dir(obj) 可以返回对象内所有的属性列表。它能够调用任何有属性的对象

也可以把类型名称传给 dir() ,可以得到相应的结果

3.文档字符串:对象的 .__doc__ 属性。


文档字符串是放在模块文件、函数、类定义的顶端,在任何可执行代码之前。Python会自动封装这个字符串,使其成
为相应对象的 .__doc__ 属性。

模块文件、函数、类定义的多行注释一般用三重引号字符串

4.标准PyDoc工具用Python代码编写,它提取文档字符串并自动格式化其信息,展示成排列友好的报表。

Python在标准库中附带了PyDoc工具
有两种最主要的PyDoc接口:
内置的 help() 函数。可以对函数、方法、类型、对象使用 help() 函数。
对较大的对象(如模块、类型), help() 输出的内容会分成几段。
报表中的信息有些是文档字符串,有些是PyDoc自动查看对象内部而收集的结构化信息。
PyDoc的 GUI|HTML 接口。启动 Tools/scripts 目录下的 pydoc3.py 脚本:
pydoc3.py -b

5.Python标准手册随Python发布。手册以HTML和其他格式实现。最重要的项目是:

Library Reference:标准库文档,说明了内置类型、函数、异常等等
Language Reference:官方给出的语言层次的细节

本文档使用 书栈(BookStack.CN) 构建 - 54 -
17. 函数

函数
1.Python的函数是由 def 语句编写。Python中,函数也是一种对象类型

def 是一个可执行语句。Python与行了 def 语句后函数对象才存在,之前该函数对象不存在


Python运行到 def 语句时,它将会生成一个新的函数对象,并将该函数对象赋值给这个函数名。函数名成了这
个函数对象的引用
lambda 表达式创建一个函数对象并返回它,但是它并没有绑定一个名字即它是匿名的没有函数名
return 语句将一个结果对象发送给调用者
yield 语句使得函数成为一个生成函数

2.函数参数没有任何类型约束,返回类型也没有任何类型约束

3.Python函数定义语句:

1. def func_name(arg1,arg2):

2. statement

在执行完 def 语句时,Python创建一个函数对象并将它赋值给 func_name 变量。

return 语句是可选的。若无 return ,则默认自动返回 None 对象


def 语句可以出现在任何语句可以出现的地方,甚至是嵌套在其他语句中。
与C语言不同,Python函数在程序运行之前不需要全部定义。 def 在它定义的地方时才评估,而 def 的之内的
代码在函数调用的时候才求值。

4.函数仅仅是对象类型,函数名也仅仅是变量名,它们没有任何特殊之处。

函数对象有函数调用方法 operator ()

函数对象允许任意的属性添加 func.attr=value

5.函数主体内的代码直到函数被调用时才运行。函数内的变量名在函数实际执行之前都不会解析。

本文档使用 书栈(BookStack.CN) 构建 - 55 -
18. 作用域

作用域
1.代码中变量名被赋值的位置决定了这个变量名的作用域(即可见范围)。因为变量是在首次赋值的时候定义的。

Python将一个变量名首次被赋值的地点关联为一个特定的命名空间

2.变量可以在3个不同的地方定义,对应三种不同的作用域:

def 内赋值定义的变量:作用域为本函数
这里的赋值包括显式 = 赋值和隐式赋值。隐式赋值包括

import 语句隐式赋值
def 函数定义来隐式赋值(函数名这个变量)
形参匹配来隐式赋值

嵌套的 def 中赋值定义的变量:对于父函数来说,该变量不是本地的


def 之外赋值,作用域为整个文件全局的

3.作用域法则:

每个模块文件就是一个全局作用域。从外面看,模块的全局变量就成为该模块对象的属性;从内部看,模块的全
局变量就是普通的、作用域为全局的变量
全局作用域的范围仅限于变量所在的单个文件
每次调用函数,都创建一个新的局部作用域
默认情况下,所有函数定义内部的变量都是局部变量
global 语句声明会将变量名作用域提升至全局
nonlocal 语句声明会将变量名作用域提升至外层的 def

Python预定义的 _builtin_ 模块提供了一些预定义的内置变量

4.交互式运行的代码实际上位于 __main__ 的内置模块中


5.变量名查找规则: LGBE

首先查找本地作用域 L

接着查找上一层 def 或 lambda 的本地作用域 E

接着查找全局作用域 G

最后查找内置作用域 B

如果均未找到变量名则报错。
当前面作用域的变量名覆盖内置的作用域名字时,可以手动 import builtins 模块,在用 builtins.name 来直接使用
这个变量名。注意不要随意修改 builtins 模块内变量的值。

1. a=1
2. b=2

3. def func():
4. global a,b,c #一个名字或逗号分隔的多个名字,这些变量名被提升到全局作用域内

本文档使用 书栈(BookStack.CN) 构建 - 56 -
18. 作用域

5. a=2
6. b=0
7. c=0 #虽然c没有在def外定义,但这里的global 和 c=0会在全局作用域内定义c

7.要在局部作用域中修改全局变量,方法有以下几种:

global声明
通过 import modname 然后利用 modname.attr 来访问
通过 import sys 然后利用 sys.modules['modname'].attr 来访问

8.作用域示例:

1. x='global_x' #全局作用域
2. def f1():
3. x='f1_x' #f1的局部作用域

4. z='f1_z' #f1的局部作用域

5. def f2():
6. global y #全局作用域

7. y='f2_y'
8. print('in f2',x) #LGBE法则,找到的是f1局部作用域中的x

9. nonlocal z #f2的nonlocal作用域,是f1的局部作用域
10. z='f2_z'

11. t='f2_t' #f2的本地作用域


12. f2()

9.嵌套的函数返回后也是有效的。

1. def f1():
2. x=99

3. def f2():
4. print(x)
5. return f2 # f2是个局部变量,仅在f1执行期间因为新建了局部作用域所以才存在。

6. action=f1() #f2指向的函数对象,由于action引用它,因此不会被收回。
7. #但是f2这个位于局部作用域中的变量名被销毁了

8. action() #此时f1执行结束,但是f2记住了在f1嵌套作用域中的x。这种行为叫闭包

类比闭包在语义上更明确,因为类可以明确定义自己的状态

10.在函数内部调用一个未定义的函数是可行的,只要在函数调用是,该未定义函数已定义即可。

本文档使用 书栈(BookStack.CN) 构建 - 57 -
18. 作用域

11.嵌套作用域中的变量在嵌套的函数调用时才进行查找,而不是定义时。 、

1. def func():

2. acts=[]
3. for i in range(5):
4. acts.append(lambda x:i**x) #添加匿名函数对象
5. return acts

6. acts=func()
7. acts[0](2) #调用时才开始查找i,此时i最后被记住的值是4

要解决这个这个陷阱,可以用默认参数:

1. def func():
2. acts=[]

3. for i in range(5):
4. acts.append(lambda x,i=i:i**x) #每次循环,形参i的默认实参均不同
5. return acts

6. acts=func()
7. acts[0](2)

12. nonlocal 是Python3中引入的。

1. def func():

2. a=0
3. b=1

4. def func1():
5. nonlocal a,b#限定了查找只能在func1所在的作用域(即func的本地作用域),且要求名字a,b已经存在。

6. a='a' #对a,b的赋值会影响到func中的a,b

如果没有 nonlocal ,则在 func1 中的赋值会创建本地变量,而无法修改 func 中的局部变量


global 使得作用域查找先从全局作用域开始,跳过了局部作用域以及 nonlocal 作用域。
Python在函数创建的时候解析 nonlocal ,而不是在函数调用时,因此要求 nonlocal 的名字已经存在

13.全局变量、 nonlocal 变量、类、函数属性都提供了状态保持能力

14. global 、 nonlocal 只是修改了变量作用域(即作用域提升),但是并未给出变量定义

本文档使用 书栈(BookStack.CN) 构建 - 58 -
19. 参数

参数
1.参数传递的性质:

参数的传递是通过自动将对象赋值给本地变量名来实现的(自动的隐式赋值)
被传递的参数对象本身并不会被拷贝

在函数内部的参数名赋值不会影响到实参对象,只是将变量名重新引用到另一个对象

若实参对象为可变对象,则在函数内原地修改参数对象会影响所有的指向该实参对象的引用

可变参数实参对象对函数来说,既可以作为输入也可以作为输出

如果想限制对可变实参对象的修改,可以创建一个可变对象的拷贝作为参数;
或者直接转换成不可变对象作为参数

2.默认情况下,参数匹配是通过其位置进行匹配的,从左到右一一匹配。必须精确传递和函数签名中参数名一样多的
实参。

3.关键字参数:允许通过变量名进行匹配,而不是通过位置。其中关键字顺序可以任意。

4.默认参数:函数定义时,可以为参数设定默认值,这样允许调用时传递较少的参数。

对于默认实参,可以不用给它传入实参

默认实参后面不能跟随非默认实参。如果出现这种情况则报语法错误。

5.可变参数:

函数能用特定的参数(以 * 开头),收集任意多的额外位置参数,将收集到的位置相关的参数到一个新元组
中。

若出现了额外的关键字参数,则报错

函数能用特定的参数(以 ** 开头),收集任意多的额外关键字参数,将收集关键字相关的参数到一个新字典
中。

若出现了额外的位置参数,则报错

6.可变参数解包:

本文档使用 书栈(BookStack.CN) 构建 - 59 -
19. 参数

调用者可以用 * 语法将实参(如元组、列表、 set )打散,形成位置参数

调用者可以用 ** 语法将字典实参打散,形成关键字参数

这里的 * 和 ** 均是在函数调用中出现,而不是在函数定义中出现

7. keyword-only 参数:在Python3中,它是一种命名参数,出现在 * 参数之后,在 ** 参数之前。所有


的 keyword-only 参数必须使用关键字语法传递。

如果不这么做,则没有办法传递该实参

8.函数调用时,位置参数与关键字参数可以组合

不能为同一个形参同时指定位置实参与关键字实参
任何关键字实参必须位于任何位置实参之后

9.函数定义时的参数类型顺序:

1. def func(a,b,c='c',*d,e,f='f',**g):
2. pass

3. # a,b:为一般参数
4. # c:指定了默认实参

5. # d:为可变位置参数
6. # e,f:为 keyword-only参数,其中f指定了默认参数

7. # g:为可变关键字参数

调用时必须先赋值形参 c ,才能进入 d 。无法跳过 c 去赋值 d

e,f,g 调用时必须都是关键字实参

10.函数调用时实参类型顺序:

1. func('a','b',e='e',*seq,**dic)
2. #seq是一个序列,它解包之后优先覆盖c,剩下的再收集成元组传给d
3. #dic是一个字典,它解包之后优先考虑e,f,剩下的在收集成字典传递给g

4. #e='e'这个关键字实参也可以位于'b'之后的任何位置

5. #关键字实参必须位于位置实参之后

通过位置分配位置参数
通过匹配变量名在分配关键字参数
额外的非关键字参数分配到 d 引用的元组中
额外的关键字参数分配到 g 引用的字典中

本文档使用 书栈(BookStack.CN) 构建 - 60 -
19. 参数

默认值分配给剩下未赋值的参数

Python最后检测确保每一个参数只传入了一个值

11.定义函数时,形参列表中 * 可以单独出现。此时函数并不表示接受一个可变长度的实参列表,而是表示 * 后
面的所有实参必须作为关键字实参传入:

并不能用 ** 像 * 一样单独出现在函数定义中。

本文档使用 书栈(BookStack.CN) 构建 - 61 -
20. 函数的高级特性

函数的高级特性
1.在Python中,函数的递归通常比 for 循环要慢而且空间消耗大,但是递归的优点是可以遍历任意形状的结构。

2.Python函数是对象,自身存储在内存块中,它可以自由地传递与引用。

函数对象支持一个特殊操作:有括号 () 以及参数列表执行调用行为
我们可以通用地检查函数对象的某些属性:如 .__name__ 属性、 .__code__ 属性
可以向函数对象附加任意的用户自定义属性,如 func.count=0 。这样的属性可以用来直接将状态信息附加到函
数对象上

Python3中,可以给函数对象附加注解。注解不作任何事情,而且注解是可选的,它被附加在函数对象
的 .__annotaions__ 属性中。注解的格式为:

1. def func(a:'a',b:(1,10),c:float) -> int:


2. return a+b+c

注解分两种:参数注解紧随形参名字的冒号 : 之后;返回值注解紧随参数列表的 -> 之后


当出现注解时,Python将它们收集到字典中并附加到 .__annotations__ 属性中
注解可以与默认值同时出现,此时形参形式为 c:float=4.0

注解只有在 def 中有效,在 lambda 表达式中无效

3.匿名函数: lambda 表达式


lambda 表达式创建了一个函数对象,它返回该函数对象而不是将其赋值给一个变量名。
创建 lambda 表达式的语法为:

1. labda arg1,arg2,...argN: expression using args

lambda 表达式是个表达式而不是语句,它能出现在不允许 def 出现的某些地方,比如参数中

lambda 表达式返回一个值(一个新的函数对象),可以将它手动赋值给一个变量名

def 总是将一个新的函数对象自动赋值给一个变量名(函数名)

lambda 的主体是一个单一的表达式,而不是一个代码块。因此 lambda 通常比 def 功能简单

lambda 内部甚至不能使用 if 语句

lambda 主体中表达式的值就是调用时的返回值,不需要显式 return

lambda 表达式也能使用默认实参
lambda 表达式主体中遵循 def 内一样的名字作用域查找法则

4.出于可读性要求,最好不要嵌套使用 lambda 表达式

本文档使用 书栈(BookStack.CN) 构建 - 62 -
20. 函数的高级特性

5. lambda 表达式应用于 map() 、 filter() 、 reduce() 等函数中较多

6.Python是静态检测局部变量的:

1. x='global'

2. def func():
3. print(x)
4. x=3

编译时,Python看到了赋值语句 x=3 ,因此决定了在函数内的任何地方, x 都是本地变量。但是 print(x) 时


赋值语句并未发生,此时即使全局中有全局的 x ,也会报错。

任何在函数体内的赋值、 import ,嵌套 def ,嵌套类等都受这种行为影响

即只要有局部变量的定义,无论在函数内哪个地方,其作用域都是本局部作用域全域

7.Python在内部会将每个默认实参保存成对应的对象,附加在这个函数本身。在不同的函数调用期间,这些默认实参
会保存同一个对象。因此对于可变对象作为默认实参注意保持警惕。

本文档使用 书栈(BookStack.CN) 构建 - 63 -
21. 模块

模块
1.从实际角度,模块对应Python程序文件(或者用外部语言如C|C#编写的扩展)。从逻辑上看,模块是最高级别的
程序组织单元

每个Python程序文件都是一个模块
模块导入另一个模块后,可以直接使用被导模块定义的全局变量名

2.Python程序是作为一个主体的、顶层文件来构造,配合零个或者多个支持的模块文件

3.Python自带了很多模块,称为标准链接库。他们提供了很多常用功能

4.导入模块用 import 。其通用格式为 import modname 。其中 modname 为模块名,它没有文件后缀名 .py ,也


没有文件路径名。
导入并非是C语言的 #include 。导入其实是运行时的运算。程序首次导入一个模块时,执行三个步骤:

找到模块文件
编译成字节码(即 .pyc 文件)。如果字节码文件不存在或者字节码文件比源代码文件旧,
则执行该步骤。否则跳过该步骤直接加载字节码
执行模块代码来创建其定义的对象

在这之后导入相同模块时,会跳过这三步而只是提取内存中已经加载的模块对象。

从内部看,Python将加载的模块存储到一个名为 sys.modules 的字典中,键就是模块名字符串。在每次导入模块开始时都检查这个字典,若模块不存


在则执行上述三步。

5.当文件 import 时,会进行编译产生字节码文件 .pyc ,因此只有被导入文件才会在机器上留下 .pyc 文件。顶


层文件的字节码在内部使用后就丢弃了,并未保留下来。

顶层文件通常设计成直接执行,而不是被导入的

6.Python模块文件搜索路径:

程序主目录
环境变量 PYTHONPATH 指定的目录
标准链接库目录(这个一般不动它)
任何 .pth 文件的内容,其中 .path 文件在前三者中查找到的。
Python会将每个 .pth 文件的每行目录从头到尾添加到 sys.path 列表的最后
(在此期间Python会过滤 .pth 文件中目录列表中重复的和不存在的目录)

以上四者优先级从高到低。这四部分组合起来就是 sys.path 列表的内容

7. sys.path 列表就是模块的搜索路径。Python在程序启动时配置它,自动将顶级文件的主目录(或代表当前工作
目录的一个空字符串)、环境变量 PYTHONPATH 指定的目录、标准库目录以及已创建的任何 .pth 文件的内容合并

本文档使用 书栈(BookStack.CN) 构建 - 64 -
21. 模块

模块搜索时,从左到右搜索 sys.path ,直到第一次找到要 import 的文件

8. import 模块时,省略文件后缀名因为模块可能是 .py 文件、 .pyc 文件,或者扩展的C模块等。

9.创建模块:任何保存为 .py 文件的文件均被自动认为是Python模块。所有该模块顶层指定的变量均为模块属性。

可执行但不会被导入的顶层文件不必保存为.py文件

因为模块名在Python中会变成变量名,因此模块名必须遵守普通变量名的命名规则

10. import 和 from 语句:

import 使得一个变量名引用整个模块对象: import module1

from 将一个变量名赋值给另一个模块中同名的对象: from module1 import printer 。在本模块内 printer 名字


引用了 module1.printer 对象
from * 语句将多个变量名赋值给了另一个模块中同名的对象: from module1 import * 。在本模块内,所
有 module1.name 对象赋值给了 name 变量名

有几点需要注意:

from 语句首先与 import 一样导入模块文件。但它多了一步:定义一个或多个变量名指向被导入模块中的同名


对象
from 与 import 都是隐性赋值语句
from 与 import 对本模块的命名空间影响不同: from 会在命名空间中引入 from import 的变量名而不会引入
模块名,
import 会在命名空间中引入模块名

from 、 import 与 def 一样是可执行语句,而不是编译器声明


当出现交叉导入时,可以使用 import ,用 from 可能出现死锁的问题: modA 需要 from import modB 的变

,而此时 modB 也在 from import modA 的变量

11.要修改被导入的全局变量,必须用 import ,然后用模块名的属性修改它;不能用以 from 隐式创建的变量名来


修改。

12.用 from 时,被导入模块对象并没有赋值给变量名:

import module1 : module1 既是模块名,也是一个变量名(引用被导入模块对象)


from module1 import func : module1 仅仅是模块名,而不是变量名

13. from 语句陷阱:

本文档使用 书栈(BookStack.CN) 构建 - 65 -
21. 模块

from 语句可能破坏命名空间
from 后跟随 reload 时, from 导入的变量名还是原始的对象

14.模块的命名空间可以通过属性 .__dict__ 或者 dir(modname) 来获取

在Python内部,模块命名空间是作为字典对象存储的
我们在模块文件中赋值的变量名在Python内部称为命名空间字典的键

15.一个模块内无法使用其他模块内的变量,除非明确地进行了导入操作

17.重载函数 reload() :它会强制已加载的模块代码重新载入并重新执行

reload 函数可以修改程序的一部分,而无需停止整个程序
reload 函数只能用于Python编写的模块,而无法用于其它语言编写的扩展模块

18. reload() 与 import 和 from 的差异:

reload 是Python内置函数,返回值为模块对象, import 与 from 是语句


传递给 reload 是已经存在的模块对象,而不是一个变量名
reload 在Python3.0中位于 imp 标准库模块中,必须首先导入才可用。

19. reload 工作细节:

reload 并不会删除并重建模块对象,它只是修改模块对象。即原来模块的每个属性对象内存空间还在,所有旧
的引用指向他们,新的引用指向修改后的属性对象内存空间
reload 会在模块当前命名空间内执行模块文件的新代码
reload 会影响所有使用 import 读取了模块的用户,用户会发现模块的属性已变
reload 只会对以后使用 from 的代码造成影响,之前用 from 的代码并不受影响。之前的名字还可用,且引用
的是旧对象

本文档使用 书栈(BookStack.CN) 构建 - 66 -
22. 模块包

模块包
1. import 时,也可以指定目录。目录称为包,这类的导入称为包导入。

包导入是将计算机上的目录变成另一个Python命名空间,它的属性对应于目录中包含的子目录和模块文件

包导入的语法:

1. import dir1.dir2.modname
2. from dir1.dir2.modname import x

包导入语句的路径中,每个目录内部必须要有 __init__.py 这个文件。否则包导入会失败

__init__.py 就像普通模块文件,它可以为空的
Python首次导入某个目录时,会自动执行该目录下 __init__.py 文件的所有程序代码
import dir1.dir2.modname 包导入后,每个目录名都成为模块对象
(模块对象的命名空间由该目录下的 __init__.py 中所有的全局变量定义
(包含显式定义和隐式定义)决定)
__init__.py 中的全局变量称为对应目录包的属性

2.任何已导入的目录包也可以用 reload 重新加载,来强制该目录包重新加载

reload 一个目录包的用法与细节与 reload 一个模块相同

3.包与 import 使用时输入字数较长,每次使用时需要输入完整包路径。可以用from语句来避免

4.包相对导入: from 语句可以用 . 与 .. :

1. from . import modname1 #modname1与本模块在同一包中(即与本文件在同一目录下)

2. from .modname1 import name #modname1与本模块在同一包中(即与本文件在同一目录下)


3. from .. import modname2 #modname2在本模块的父目录中(即在本文件上层)

Python2中, import modname 会优先在本模块所在目录下加载 modname 以执行相对导入。


因此局部的模块可能会因此屏蔽 sys.path 上的另一个模块
要想启用相对导入功能,使用 from __future__ import absolute_import

Python3中,没有点号的导入均为绝对导入。 import 总是优先在包外查找模块

本文档使用 书栈(BookStack.CN) 构建 - 67 -
23. 模块高级用法

模块高级用法
1.Python模块会默认导出其模块文件顶层所赋值的所有变量名,不存在私有变量名。所有的私有数据更像是一个约
定,而不是语法约束:

下划线开始的变量名 _x : from * 导入该模块时,这类变量名不会被复制出去

模块文件顶层的变量名列表 __all__ :它是一个变量名的字符串列表。 from * 语句只会把列在 __all__ 列表中


的这些变量名复制出来。

Python会首先查找模块内的 __all__ 列表;否该列表未定义,则 from * 会复制那些非


_ 开头的所有变量名
所有这些隐藏变量名的方法都可以通过模块的属性直接绕开

2.当文件是以顶层程序文件执行时,该模块的 __name__ 属性会设为字符串 "__main__" 。若文件被导入,


则 __name__ 属性就成为文件名去掉后缀的名字

模块可以检测自己的 __name__ 属性,以确定它是在执行还是被导入

使用 __name__ 最常见的是用于自我测试代码:在文件末尾添加测试部分:

1. if __name__=='__main__':
2. #pass

3.在程序中修改 sys.path 内置列表,会对修改点之后的所有导入产生影响。因为所有导入都使用同一


个 sys.path 列表

4. import 和 from 可以使用 as 扩展,通过这种方法解决变量名冲突:

1. import modname as name1

2. from modname import attr as name2

在使用 as 扩展之后,必须用 name1 、 name2 访问,而不能用 modname 或者 attr ,因为它们事实上


被 del 掉了

5.在 import 与 from 时有个问题,即必须编写变量名,而无法通过字符串指定。有两种方法:

使用 exec: exec(“import “+modname_string)`

使用内置的 __import__ 函数: __import__(modname_string) ,它返回一个模块对象

这种方法速度较快

本文档使用 书栈(BookStack.CN) 构建 - 68 -
23. 模块高级用法

6. reload(modname) 只会重载模块 modname ,而对于模块 modname 文件中 import 的模块, reload 函数不会自
动加载。
要想 reload 模块 A 以及 A import 的所有模块,可以手工递归扫描 A 模块的 __dict__ 属性,并检查每
一项的 type 以找到所有 import 的模块然后 reload 这些模块

7.可以通过下列几种办法获取模块的某个属性:

modname.attr :直接通过模块对象访问
modname.__dict__['attr'] :通过模块对象的 __dict__ 属性字典访问
sys.modules['modname'].name :通过Python的 sys.modules 获取模块对象来访问
getattr(modname,'attr') :通过模块对象的 .getattr() 方法来访问

本文档使用 书栈(BookStack.CN) 构建 - 69 -
24. 类 class

类 class
1.Python中,类 class 与实例 instance 是两种不同的对象类型

类对象是实例对象的工厂
类对象与实例对象都有各自独立的命名空间
类对象来自于类定义语句,实例对象来自于函数调用语句
实例对象可自动存取类对象中的变量名

2.类的定义:

1. class class_name:

2. name1=val
3. def method(self):
4. pass

class 语句类似于 def 语句,也是可执行语句。执行时会产生新的类对象,并将该类对象赋值给变量名(即类


名)
class 语句内的顶层赋值语句会创建类的属性。 class 语句创建的作用域会成为类属性的命名空间
如果是 class 内的 def 中的赋值语句,则并不会创建类的属性

类属性为所有的实例对象提供状态和行为,它是由该类创建的所有实例对象共享的

3.生成实例对象:

1. instance_name=class_name()

像函数那样调用类对象会创建新的实例对象。
每次调用时,均会返回新的实例对象
每个实例对象都有自己的命名空间。同一个类的实例对象不一定属性都相同
每一个实例对象继承类的属性并创建了自己的命名空间
类创建的实例对象是有新的命名空间。刚开始该命名空间是空的,
但它会继承创建该实例所属类对象的属性。
继承的意思是,虽然实例对象的命名空间是空的。
但是名字查找会自动上升到类对象的名字空间去查找

在类的 def 中,第一个参数(根据惯例称为 self )会引用调用该函数的实例对象。对 self 的属性赋值,


会创建或修改实例对象的属性,而非类的属性。

可以通过方法调用: instance_name.func()

也可以通过类调用: class_name.func(instance_name)

本文档使用 书栈(BookStack.CN) 构建 - 70 -
24. 类 class

4.与C++不同,Python的 class 语句是一个可执行语句,且是隐式赋值的。

5.可以在 class 语句外创建类对象的新属性,通过向类对象直接赋值来实现。

1. classA.attr=val

可以在 class 语句外创建实例对象的新属性,通过向实例对象直接赋值来实现。

1. a=classA()
2. a.attr=val

6.类可以继承。被继承的类称为超类,继承类称为子类。

本文档使用 书栈(BookStack.CN) 构建 - 71 -
24. 类 class

1. class childC (parentC): # childC为子类,parentC为超类

2. pass

类对象会继承其超类对象中定义的所有类属性名称。读属性时,若该属性不存在于本类中,Python会自动在超
类的命名空间中寻找
若是写属性,则会创建新属性(此时仅操作本类的命名空间)

实例对象会继承所有可访问到的类的属性。读变量名时,Python会首先检查实例的命名空间,然后是类的命名
空间,最后是沿继承链查找所有超类的命名空间
若子类重新定义了超类的变量名(无论是在 class 内部定义,还是在 class 外部通过赋值来定义),子类会取
代并定制所继承的行为。这称为重载

7.类对象的 .__dict__ 属性是类对象的命名空间,是一个类字典对象 mappingproxy 对象 ;


实例对象的 .__dict__ 属性是实例对象的命名空间,是一个字典;
通过查看它们可以轻易地查看到继承树的各属性

8.实例对象的 .__class__ 属性是它所属的类


类对象的 __bases__ 属性是它超类对象的元组
类对象的 __name__ 属性是类名

本文档使用 书栈(BookStack.CN) 构建 - 72 -
24. 类 class

9.在子类中调用超类的方法: superClass.func(obj,args) ,其中 obj 通常为 self

10.Python的继承与C++继承不同。在Python中,当对象通过点号运算读取属性值时就会发生继承,而且涉及了搜索
属性定义树。

每次使用 name.attr 时( name 为实例对象或者类对象),Python会从底部向上搜索命名空间树。先从本对象的


命名空间开始,一直搜索到第一个找到的 attr 名字就停止
命名空间树中较低位置处的定义会覆盖较高位置处的定义
继承树的搜索仅仅发生在读取属性值的时候。在写属性值时,执行的是属性的定义(当前命名空间中该名字不存
在)或赋值(当前命名空间中该名字已存在)语义。

1. graph BT;

2. A(实例命名空间)-->B[类命名空间];
3. B-->C[超类1命名空间];

4. B-->D[超类2命名空间];

5. style A fill:#f9f,stroke:#333;

11.类对象与实例对象都是内存中的临时对象。可以通过对象持久化来把他们保存在磁盘中。

pickle 模块:通用的对象序列化与反序列化工具。它可以将任何对象转换为字节串,以及将该字节串在内存
中重建为最初的对象。 pickle 常用接口为:

序列化:
pickle.dump(obj, file, protocol=None, *, fix_imports=True) : 将 obj 对象序列化并写入 file 文
件对象中
pickle.dumps(obj, protocol=None, *, fix_imports=True) :将 obj 对象序列化并返回对应的字节串对
象(并不写入文件中)

本文档使用 书栈(BookStack.CN) 构建 - 73 -
24. 类 class

反序列化:

pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict") :从 file 对象中保存的


字节串中读取序列化数据,反序列化为对象
pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict") :从字节串中
读取序列化数据,反序列化为对象

dbm 模块:一个可以通过键访问的文件系统.它的键、值都是字节串或者字符串。它以类似字典的方式访问
(但是首先要 open )。

shelve 模块:以上两个模块按照键将Python对象存/取到一个文件中。 shelve 模块提供了一个额外的结构


层。允许按照键来存储 pickle 处理后的对象

12. shelve 模块用法:它用 pickle 把对象转换为字节串,并将其存储在一个 dbm 文件的键之下;它通过键获


取 pickle 化的字节串,并用 pickle 在内存中重新创建最初的对象

一个 shelve 的 pickle 化对象看上去就像字典。 shelve 自动把字典操作映射到存储|读取在文件中的对象


一个 shelve 和常规字典用法上的唯一区别:
一开始必须打开 shelve 并且在最后关闭它。另外 shelve 不支持 SQL 查询工具

存取语法

存储的语法:

1. import shelve
2. db=shelve.open('filename') #打开

3. for obj in objList:


4. db[obj.name]=obj #写入

本文档使用 书栈(BookStack.CN) 构建 - 74 -
24. 类 class

5. db.close() #关闭

读取的语法:

1. import shelve
2. db=shelve.open('filename') #打开
3. for key in db:#像字典一样访问
4. print(key,'=>',db[key]) #读取

5. db.close() #关闭

载入重建存储的对象时,不必 import 对象所属类。因为Python对一个对象进行 pickle 操作时,记录


了 self 实例属性,以及实例所属类的名字和类的位置。当 shelve 获取实例对象并对其进行 unpickle 时,
Python会自动重新 import 该类。

优缺点:

缺点: pickle 作用的类必须在一个模块文件顶部编码,且该模块文件可通过 sys.path 找到


优点:当类实例对象再次重建时,对类的源代码文件的修改会自动选取。
这一般发生在两次运行时。若仅仅在一次运行中,则前面已import,再次import无效果

13.类可以有文档字符串。文档字符串是位于各种结构顶部的字符串常量。

文档字符串在运行时能保持
可以通过 .__doc__ 属性获取

本文档使用 书栈(BookStack.CN) 构建 - 75 -
25. Python命名空间

Python命名空间
1.有点号和无点号的变量名,会使用不同的方式处理

无点号运算符的变量名,使用 LEGB 作用域规则查找名字


有点好运算符的变量名,使用的是对象的命名空间查找名字
模块的作用域以及类的作用域会对对象的命名空间进行初始化

2.模块、类、实例对象的命名空间实际上是以字典的形式实现的,并由内置属性 .__dict__ 表示

属性点号运算其实内部就是字典的索引运算
属性继承其实就是搜索链接的字典
每个实例都有各自独立的命名空间字典。初始时为空字典。
随着对 self 或者对象的属性赋值,命名空间字典不断扩张

3.读取属性可以通过点运算符或者直接通过键索引:

1. obj.attr #通过点运算符
2. obj.__dict__['attr']#通过键索引

通过键索引时必须给出属性名字符串;通过点运算符时给出的是属性名(不是字符串)

4. dir 函数可以输出对象的所有可访问属性,包括它继承的名字(以及其他一些内置的系统属性)

本文档使用 书栈(BookStack.CN) 构建 - 76 -
26. 运算符重载

运算符重载
1.运算符重载只是意味着在类方法中拦截内置的操作

运算符重载让类拦截常规的Python运算
类可以重载所有的Python表达式运算符。当实例对象作为内置运算符的操作数时,这些方法会自动调用
类也可以重载打印、函数调用、属性点号运算符等内置运算
重载可以使得实例对象的行为更像内置类型

重载运算符通常并不是必须的,也不是默认的行为

2.Python中所有可以被重载的方法名称前、后均有两个下划线字符,以便将它与其他类内定义的名字区分开来,
如 __add__

3.若使用未定义运算符重载方法,则它可能继承自超类。若超类中也没有则说明你的类不支持该运算,强势使用该运
算符则抛出异常

4. .__init__(self,args) 方法:称为构造函数。当新的实例对象构造时,会调用 .__init__(self,args) 方法。它用于


初始化实例的状态

本文档使用 书栈(BookStack.CN) 构建 - 77 -
26. 运算符重载

5. .__getitem__(self,index) 和 .__setitem(self,index,value) 方法:

对于实例对象的索引运算,会自动调用 .__getitem__(self,index) 方法,将实例对象作为第一个参数传递,方括


号内的索引值传递给第二个参数

对于分片表达式也调用 .__getitem__(self,index) 方法。实际上分片边界如 [2:4] 绑定到了一个 slice 分片


对象上,该对象传递给了 .__getitem__ 方法。
对于带有一个 .__getitem__ 方法的类,该方法必须既能针对基本索引(一个整数),又能针对分片调用(一
个 slice 对象作为参数)

.__setitem(self,index,value) 方法类似地拦截索引赋值和分片赋值。第一个参数为实例对象,第二个参数为基
本索引或者分片对象,第三个参数为值

6. .__index__(self) 方法:该方法将实例对象转换为整数值。即当要求整数值的地方出现了实例对象时自行调用。

7. .__getitem__(self,index) 也是Python的重载迭代方式之一。一旦定义了这个方法, for 循环每一次循环时可以


调用 .__getitem__(self,index) 方法。因此任何响应了索引运算的内置或者用户自定义的实例对象通用可以响应迭
代。

for 可以检测到超出边界异常 IndexError ,从而终止迭代

本文档使用 书栈(BookStack.CN) 构建 - 78 -
26. 运算符重载

8.目前Python的所有迭代环境都会首先尝试调用 .__iter__(self) 方法,再尝试调用 .__getitem__(self,index) 方


法。

.__iter__(self) 方法必须返回一个迭代器对象。Python的迭代环境通过重复调用这个迭代器对象
的 .__next__(self) 方法,直到发生了 StopIteration 异常
.__iter__(self) 返回的迭代器对象会在调用 .__next__(self)

的过程中明确保留状态信息,因此比 .__getitem__(self,index) 方法具有更好的通用性


迭代器对象没有重载索引表达式,因此不支持随机的索引运算
.__iter__(self) 返回的迭代器只能顺序迭代一次。
因此每次要进行新的一轮循环时必须创建一个新的迭代器对象

对于调用 .__getitem__(self,index) 的环境,Python的迭代环境通过重复调用该方法,其中 index 每轮迭代


中从 0 依次递增,直到发生了 IndexError 异常

本文档使用 书栈(BookStack.CN) 构建 - 79 -
26. 运算符重载

9.要让实例对象支持多个迭代器, .__iter__(self) 方法必须创建并返回新的迭代器对象。

每个激活状态下的迭代器都有自己的状态信息,而不管其他激活状态下的迭代器

10.类通常把 in 成员关系运算符实现为一个迭代,用 .__iter__(self) 方法或 .__getitem__(self,index) 方法。也


能实现 .__contains__(self,value) 方法来实现特定成员关系。

.__contains__(self,value) 方法优先于 .__iter__(self) 方法, .__iter__(self) 方法优先

本文档使用 书栈(BookStack.CN) 构建 - 80 -
26. 运算符重载

于 .__getitem__(self,index) 方法采纳

11. .__getattr__(self,'name') 方法:拦截属性点号运算 obj.name 。只有当对未定义(即不存在)的属性名称进行


点号运算时,实例对象会调用此方法

当Python可以从继承树中找到该属性名时,并不会调用 .__getattr__(self,'name') 方法

属性不仅仅是变量名,也可以是方法名

内置的 getattr(obj,'name') 函数等价于调用 obj.name ,它执行继承搜索。搜不到时调


用 .__getattr__(self,“name”) 方法

本文档使用 书栈(BookStack.CN) 构建 - 81 -
26. 运算符重载

如果没有定义 .__getattr__(self,“name”) 方法,则对于不知道如何处理的属性(即找不到的),则Python抛


出内置的 AttributeError 异常

12. .__setattr__(self,'name',value) 方法:拦截所有的属性赋值语句(无论该属性名是否存在)

对于属性赋值语句,因为如果该属性曾经不存在,则一旦赋值就增加了一个新的属性

属性不仅仅是变量名,也可以是方法名

注意: .__setattr__(self,'name',value) 方法的函数体内,任何对 self 属性赋值语句( self.name=value )都会再次递


归调用 .__setattr__(self,'name',value) 函数

为了防止 .__setattr__(self,'name',value) 函数体内的无穷递归,在该方法内的 self 属性赋值要采用属性字


典索引的方法: self.__dict__['name']=value

属性不仅仅是变量名,也可以是方法名

内置的 setattr(obj,'name',value) 函数等价于调用 obj.name=value

本文档使用 书栈(BookStack.CN) 构建 - 82 -
26. 运算符重载

13. .__getattribute__(self,'name') 方法:拦截所有的属性读取,而不仅仅是那些未定义的。

注意: .__getattribute__(self,'name') 方法的函数体内,任何对 self 属性读取语句( self.name )都会再次递归


调用 .__getattribute__(self,'name') 函数。尽量不要重载 .__getattribute__(self,'name') 方法避免无穷递归

属性不仅仅是变量名,也可以是方法名

本文档使用 书栈(BookStack.CN) 构建 - 83 -
26. 运算符重载

14.通过 .__getattr__ 与 .__setattr__ 方法混合使用可以模拟实例对象的私有属性:

实例对象保存一个 self.private 变量名列表


对 .__setattr__ 与 .__getattr__ ,判断属性名是否在 self.private 变量名列表中。
若是,则抛出异常
对于通过 obj.__dict__['name'] 访问,可以绕过这种机制

15. .__add__(self,value) 方法:当实例对象在加法中时调用

16. .__repr__(self) 和 .__str__(self) 方法:当实例对象在打印或者转成字符串时调用

打印会首先尝试 .__str__(self) 方法和 str(x) 内置函数。如果没有,则使用


.__repr__(self) 方法

本文档使用 书栈(BookStack.CN) 构建 - 84 -
26. 运算符重载

.__repr__(self) 主要应用于交互式下提示回应以及 repr 函数。如果没有,它不会调用


.__str__(self) 方法

17.加法有三种:

常规加法:实例对象在 + 左侧,由 .__add__(self,value) 拦截


右侧加法:实例对象在 + 右侧,由 .__radd__(self,value) 拦截
原地加法:实例对西在 += 左侧,由 .__iadd__(self,value) 拦截

要实现满足交换律的运算符,要同时重载 .__add__(self,value) 与 .__radd__(self,value) 方法。

当不同类的实例对象混合出现在 + 两侧时,Python优先选择左侧的那个类来拦截 +
注意当在这三种加法函数体内出现了 + 或者 += 时可能出现递归调用

原地 += 优先采用 .__iadd__(self,value) ,如果它没有重载,则采用 .__add__(self,value)

本文档使用 书栈(BookStack.CN) 构建 - 85 -
26. 运算符重载

18.每个二元运算符都有类似 + 的右侧和原地重载方法。他们以相似的方式工作。

右侧方法通常只有在需要满足交换律时用得到,一般较少使用
在实现这些方法时,函数体内注意不要出现递归调用

19. .__call__(self,*pargs,**kwargs) 方法:函数调用方法。当调用实例对象时,


由 .__call__(self,*pargs,**kwargs) 方法拦截。

.__call__(self,*pargs,**kwargs) 方法支持所有的参数传递方式

20.实例对象可以拦截6种比较运算符: < > <= >= == != ,对应于

本文档使用 书栈(BookStack.CN) 构建 - 86 -
26. 运算符重载

1. .__lt__(self, other) # <


2. .__le__(self, other) # <=

3. .__gt__(self, other) # >


4. .__ge__(self, other) # >=
5. .__eq__(self, other) # ==
6. .__ne__(self, other) # !=

比较运算符全部是左端形式,无右端形式: 3<=obj 会转换成 obj>=3

比较运算符并没有隐式关系。 == 为真,并不意味着 != 为假。


因此 .__eq__(self, other) 与 .__ne__(self, other) 必须同时实现而且语义要一致。

21.在布尔环境中,Python会首先尝试 .__bool__(self) 方法来获取一个直接的布尔值。如果没有这个方法,则 尝


试 .__len__(self) 方法根据其结果确定实例对象的真值(非0则为真,0为假)

本文档使用 书栈(BookStack.CN) 构建 - 87 -
26. 运算符重载

22.每当实例对象空间被收回时(在垃圾收集时),析构函数 .__del__(self) 自动调用。

Python的析构函数不常用。原因有二:

对于空间管理来说,通常不需要用户手动管理
用户无法预测实例对象的具体回收时机,这个时机有Python自动调度

本文档使用 书栈(BookStack.CN) 构建 - 88 -
27. 类的设计模式

类的设计模式
1.Python不会执行同名函数的重载,而只会用新对象覆盖旧对象

1. class A:
2. def func(self,x):
3. pass

4. def func(self,x,y):
5. pass

由于 class 、 def 均为可执行代码,因此 func 变量名这里被重新赋值了。

2.委托设计:在Python中委托通常以拦截 .__getattr__(self,'name') 来实现。该方法会拦截对不存在属性的读取

代理类实例对象可以利用 .__getattr__(self,'name') 将任意的属性读取转发给被包装的对象

代理类可以有被包装对象的借口,且自己还可以有其他接口

3.Python支持变量名压缩的概念: class 语句内以 __ (两个下划线)开头但是结尾没有 __ (两个下划线)


的变量名(如 __x )会自动扩张为包含所在类的名称(如 _classname__x )

变量名压缩只发生在 class 语句内,且仅仅针对 __x 这种以 __ 开头的变量名

该做法常用于避免实例中潜在的变量名冲突

本文档使用 书栈(BookStack.CN) 构建 - 89 -
27. 类的设计模式

4.Python3中,实例方法有两种形式:

普通函数方法:通过对类名进行点号运算而获得类的函数属性,如 classname.func ,会返回普通函数方法。


若调用的是实例方法,必须明确提供实例对象作为第一个参数,如 classname.func(obj,arg)

若调用的是一般的方法,则遵守普通函数调用规则即可 classname.func(arg)

绑定方法:通过对实例对象进行点号运算而获得类的函数属性,如 obj.func ,会返回绑定方法对象。Python


在绑定方法对象中,自动将实例和函数打包

绑定方法调用时,不需要手动传入实例对象,如 obj.func(arg)

绑定方法的 __self__ 属性引用被绑定的实例对象, __func__ 属性引用该类的该函数对象

5.多重继承:子类可以继承一个以上的超类。超类在 class 语句首行括号内列出,以逗号分隔

子类与其实例继承了列出的所有超类的命名空间

本文档使用 书栈(BookStack.CN) 构建 - 90 -
27. 类的设计模式

搜索属性时,Python会从左到右搜索 class 首行中的超类,直到找到匹配的名字

6.工厂函数:通过传入类对象和初始化参数来产生新的实例对象:

1. def factory(classname,*args,**kwargs):

2. return classname(*args,**kwargs)

7.抽象超类:类的部分行为未定义,必须由其子类提供

若子类也未定义预期的方法,则Python会引发未定义变量名的异常
类的编写者也可以用 assert 语句或者 raise 异常来显式提示这是一个抽象类

1. class A:
2. def func(self):

3. self.act() #该方法未实现

4. def act(self):
5. assert False, 'act must be defined!'
6. class ChildA(A):

7. def act(self):
8. print('in ChildA act')

本文档使用 书栈(BookStack.CN) 构建 - 91 -
27. 类的设计模式

9. x=child()
10. x.func()

这里的核心在于:超类中 self.act() 调用时, self 指向的有可能是真实的实例对象(子类对象)

本文档使用 书栈(BookStack.CN) 构建 - 92 -
28. 类的高级主题

类的高级主题
1.通过在自定义类中嵌入内置类型,类似委托;这样自定义类就可以实现内置类型的接口。
这些接口在内部通过操作嵌入的内置类型来实现。

这是一种扩展内置类型的方式

2.通过子类扩展内置类型:所有内置类型都可以直接创建子类,如 list 、 str 、 dict 、 tuple 、 set 等

内置类型的子类实例可以用于内置类型对象能够出现的任何地方

3.在Python3中所有的类都是新式类。

Python2.2之前的类不是新式类

所有的类都是从 object 内置类派生而来


type(obj) 返回对象实例所属的类对象
对于实例对象的 type(obj) 返回值也是 obj.__class__

type(classname) 返回 "type" ,因为所有 class 对象都是 type 的实例


由于所有 class 均直接或者间接地派生自 object 类,因此每个实例对象都是 object 类的实例

object 是 type 类的实例,但是同时 type 又派生自 object

4.Python3中的类有一个 .__slots__ 属性,它是一个字符串列表。这个列表限定了类的实例对象的合法属性名。如


果给实例赋了一个 .__slots__ 列表之外的属性名会引发异常

虽然有了 .__slots__ 列表,但是实例对象的属性还是必须先赋值才存在

本文档使用 书栈(BookStack.CN) 构建 - 93 -
28. 类的高级主题

当有 .__slots__ 列表存在时,默认会删除 .__dict__ 属性,而 getattr() , setattr()

以及 dir() 等函数均使用 .__slots__ 属性,因此仍旧可以正常工作

可以在 .__slots__ 列表中添加 .__dict__ 字符串,


因此对于使用 .__dict__ 的地方均能正常工作

.__slots__ 属性的使用可以优化内存和读取速度

在继承中:

若子类继承自一个没有 .__slots__ 的超类,则超类的 .__dict__ 属性可用,则子类中的 .__slots__

没有意义。因为子类继承了超类的 .__dict__ 属性

本文档使用 书栈(BookStack.CN) 构建 - 94 -
28. 类的高级主题

若子类有 .__slots__ ,超类也有 .__slots__ ,子类的合法属性名为父类和子类的 .__slots__ 列表的


并集

若超类有 .__slots__ ,子类未定义 .__slots__ ,则子类将会有一个 .__dict__ 属性

5.Python3的 property 机制:


property 是一个对象,通过它给类变量名赋值。

1. class A:

2. age=property(getMethod,setMethod,delMethod,docMethod)
3. # 或者直接指定docstring
4. def getMethod(self):
5. pass

6. def setMethod(self,val):
7. pass
8. def delMethod(self):
9. pass

10. def docMethod(self):

11. ...
12. # return a string

本文档使用 书栈(BookStack.CN) 构建 - 95 -
28. 类的高级主题

property 优点:代码简单,运行速度更快;缺点:当类编写时可能还无法确定 property 名字,因此无法提供动


态的接口

如果 property 的 docstring 或者 docMethod 为 None ,则Python使用 getMethod 的 docstring 。

一个添加了语法糖的方案为:

1. class A:

2. def __init__(self):
3. self._x = None

4. @property #定义了一个property get函数,必选


5. def x(self): # property name 就是 get函数的函数名

6. """I'm the 'x' property."""


7. return self._x

8. @x.setter #定义了一个property set函数,可选


9. def x(self, value):

10. self._x = value


11. @x.deleter #定义了一个property del函数,可选

12. def x(self):


13. del self._x

6.Python类中有两种特殊的方法: staticmethod 方法和 classmethod 方法

staticmethod 方法:当以实例对象调用 staticmethod 方法时,Python并不会将实例对象传入作为参数;而


普通的实例方法,通过实例对象调用时,Python将实例对象作为第一个参数传入

定义 staticmethod 方法:

1. class A:
2. @staticmethod #定义一个staticmethod
3. def func(*args,**kwargs)

4. pass

classmethod 方法:当以实例对象或者类对象调用 classmethod 方法时,Python将类对象(如果是实例对象

本文档使用 书栈(BookStack.CN) 构建 - 96 -
28. 类的高级主题

调用,则提取该实例所属的类对象)传入函数的第一个参数 cls 中

定义 classmethod 方法:

1. class A:

2. @classmethod #classmethod
3. def func(cls,*args,**kwargs)
4. pass

总结一下,类中可以定义四种方法:

普通方法:方法就是类对象的一个属性,执行常规函数调用语义 classname.method(args)

实例方法:传入一个实例作为方法的第一个实参。调用时可以:
obj.method(args) :通过实例调用
classname.method(obj,args) :通过类调用
staticmethod 方法:* obj.method(args) 通过实例调用时,执行的是 classname.method(args) 语义

classmethod 方法:* obj.method(args) 执行的是 classname.method(classname,args) 语义

本文档使用 书栈(BookStack.CN) 构建 - 97 -
28. 类的高级主题

7.类的实例方法中,用哪个实例调用的该方法, self 就是指向那个实例对象


类的 classmethod 方法中,用哪个类调用该方法, cls 就指向那个类对象

8.类对象与实例对象都是可变对象,可以给类属性、实例属性进行赋值,这就是原地修改。这种行为会影响对它的多
处引用
任何在类层次所作的修改都会反映到所有实例对象中

9.若类的某个属性是可变对象(如列表、字典),则对它的修改会立即影响所有的实例对象

本文档使用 书栈(BookStack.CN) 构建 - 98 -
28. 类的高级主题

10.多重继承中,超类在 class 语句首行内的顺序很重要。Python搜索继承树时总是根据超类的顺序,从左到右搜


索超类。

11.类的 .__mro__ 属性:类对象的 .__mro__ 属性。它是一个 tuple ,里面存放的是类的实例方法名解析时需要


查找的类。Python根据该元组中类的前后顺序进行查找。类对象的 .__mro__ 列出了 getattr() 函数以
及 super() 函数对实例方法名字解析时的类查找顺序。

类的 .__mro__ 是动态的,当继承层次改变时它也随之改变
元类可以重写一个类的 .mro() 方法来定义该类的 __.mro__ 属性。该方法在类被创建时调用,结果存放在类
的 .__mro__ 属性中

12. super() 函数: super() 返回一个 super 实例对象,它用于代理实例方法/类方法的执行

super(class,an_object) :要求 isinstance(an_object,class) 为真。代理执行了实例方法调用


super(class,class2) :要求 issubclass(class2,class) 为真。代理执行了类方法调用

有两种特殊用法:

super(class) :返回一个非绑定的 super 对象


在类的实例方法中,直接调用 super() ,等价于 super(classname,self) (这里 self 可能是 classname 子类实
例)
在类的类方法中,直接调用 super() ,等价于 super(classname,cls) (这里 cls 可能是 classname 子类)

原理: super 的原理类似于:

1. def super(cls,instance):
2. mro=instance.__class__.__mro__ #通过 instance生成 mro

3. return mro[mro.index(cls)+1] #查找cls在当前mro中的index,饭后返回cls的下一个元素

本文档使用 书栈(BookStack.CN) 构建 - 99 -
28. 类的高级主题

示例:

1. class Root:

2. def method1(self):
3. print("this is Root")
4. class B(Root):
5. def method1(self):

6. print("enter B")
7. print(self)
8. super(B,self).method1() #也可以简写为 super().method1()
9. print("leave B")

10. class C(Root):


11. def method1(self):
12. print("enter C")
13. print(self)
14. super().method1() #也可以写成super(C,self).method1()

15. print("leave C")


16. class D(B,C):
17. pass

调用 D().method1() —> D 中没有 method1

B 中找到(查找规则: D.__mro__ ) —> 执行 B 中的 method1 。此时 self 为D实例。 D.__mro__ 中, B 的


下一个是 C ,因此 super(B,self).method1() 从类 C 中查找 method1 。
执行 C 的 method1 。此时 self 为D实例。 D.__mro__ 中, C 的下一个是 Root ,因
此 super(C,self).method1() 从类 Root 中查找 method1 。
执行 Root 的 method1 。

print(self) 可以看到,这里的 self 全部为 D 的实例

类的 classmethod 依次类推

类、实例的属性查找规则没有那么复杂。因为属性变量只是一个变量,它没办法调用 super(...) 函数。


只有实例方法和类方法有能力调用 super(...) 函数,才会导致这种规则诞生

本文档使用 书栈(BookStack.CN) 构建 - 100 -


28. 类的高级主题

本文档使用 书栈(BookStack.CN) 构建 - 101 -


29. 异常

异常
1.Python中,异常会根据错误自动地被触发,也能由代码主动触发和截获

2.捕捉异常的代码:

1. try:
2. statements #该代码执行主要的工作,并有可能引起异常
3. except ExceptionType1: #except子句定义异常处理,这里捕捉特定的ExceptionType1类型的异常

4. statements
5. except (ExceptionType2,ExceptionType3): #except子句定义异常处理,
6. #这里捕捉任何列出的异常(即只要是ExceptionType2类型或者ExceptionType3类型)
7. statements

8. except ExceptionType4 as excp: #这里捕捉特定的ExceptionType4类型异常,但是用变量名excp引用异常对象


9. statements #这里可以使用excp引用捕捉的异常对象
10. except: # 该子句捕获所有异常
11. statements

12. else: #如果没有发生异常,这来到这里;当发生了异常则不执行else子句


13. statements

当 try 子句执行时发生异常,则Python会执行第一个匹配该异常的 except 子句。当 except 子句执行完毕之


后(除非该 except 子句 又引发了另一个异常),程序会跳转到整体语句之后执行。
整体语句就是指上面的 try..except..else

如果异常发生在 try 代码块内,且无匹配的 except 子句,则异常向上传递到本 try 块外层的 try 块中。如果


已经传递到了顶层了异常还没有被捕捉,则Python会终止程序并且打印默认的出错消息

如果 try 代码块内语句未产生异常,则Python会执行 else 子句,然后程序会在整体语句之后继续执行

3. try/finally 语句:

本文档使用 书栈(BookStack.CN) 构建 - 102 -


29. 异常

1. try:
2. statements

3. finally:
4. statements

无论 try 代码块执行时是否发生了异常, finally 子句一定会被执行

若 try 子句无异常,则Python会接着执行 finally 子句,执行完之后程序会跳转到整体语句之后执行

若 try 子句有异常,则Python会跳转到 finally 子句中,并接着把异常向上传递

4.Python中的 try|except|finally 统一格式:

1. try:
2. statements #该代码执行主要的工作,并有可能引起异常

3. except ExceptionType1: #except子句定义异常处理,这里捕捉特定的ExceptionType1类型的异常


4. statements

5. except (ExceptionType2,ExceptionType3): #except子句定义异常处理,


6. #这里捕捉任何列出的异常(即只要是ExceptionType2类型或者ExceptionType3类型)
7. statements

8. except ExceptionType4 as excp: #这里捕捉特定的ExceptionType4类型异常,但是用变量名excp引用异常对象


9. statements #这里可以使用excp引用捕捉的异常对象

10. except: # 该子句捕获所有异常

11. statements
12. else: # 如果没有发生异常,这来到这里;当发生了异常则不执行else子句

13. statements

14. finally: # 一定会执行这个子句

15. statements

else 、 finally 子句可选; except 子句可能有0个或者多个。但是如果有 else 子句,则至少有一个 except

finally 执行时机:无论有没有异常抛出,在程序跳出整体语句之前的最后时刻一定会执行

整体语句就是指上面的 try..except..else...finally

本文档使用 书栈(BookStack.CN) 构建 - 103 -


29. 异常

5.要显式触发异常,可以用 raise 语句。有三种形式的形式:

raise exception_obj :抛出一个异常实例


raise Exception_type :抛出一个指定异常类型的实例,调用 Exception_type() 获得
raise <exceptionObj|Exception_type> from <exceptionObj2|Exception_type2> :
第二个异常实例会附加到第一个异常实例的 .__cause__ 属性中并抛出第一个异常实例

raise :转发当前作用域中激活的异常实例。若当前作用域中没有激活的异常实例,则抛出 RuntimeError 实


例对象

一旦异常在程序中由某个 except 子句捕获,则它就死掉了不会再传递

raise 抛出的必须是一个 BaseException 实例或者 BaseException 子类,否则抛出 TypeError

BaseException 类是所有内建异常的父类。

Exception 类是所有内建异常、 non-system-exiting 异常的父类。用于自定义的异常类也应该从该类派生

本文档使用 书栈(BookStack.CN) 构建 - 104 -


29. 异常

6.在一个异常处理器内部 raise 一个异常时,前一个异常会附加到新异常的 __context__ 属性

如果在异常处理器内部 raise 被捕获的异常自己,则并不会添加到 __context__ 属性

本文档使用 书栈(BookStack.CN) 构建 - 105 -


29. 异常

在异常处理器内部 raise 与 raise e 效果相同

7. assert 语句可能会引起 AssertionError 。其用法为: assert <test>,<data> 。这等价于:

1. if __debug__:
2. if not <test>:
3. raise AssertionError(<data>)

<test> 表达式用于计算真假, <data> 表达式用于作为异常的参数。若 <test> 计算为假,则抛


出 AssertionError

若执行时用命令行 -0 标志位,则关闭 assert 功能(默认是打开的)。


__debug__ 是内置变量名。当有 -0 标志位时,它为0;否则为1

通常 assert 用于给定约束条件,而不是用于捕捉程序的错误。

本文档使用 书栈(BookStack.CN) 构建 - 106 -


29. 异常

8.Python3中有一种新的异常相关语句: with/as 语句。它是作为 try/finally 的替代方案。用法为:

1. with expression [as var]:


2. statements

expression 必须返回一个对象,该对象必须支持环境管理协议。其工作方式为:

计算 expression 表达式的值,得到环境管理器对象。环境管理器对象必须有 .__enter__(self) 方法


和 .__exit__(self, exc_type, exc_value, traceback) 方法
调用环境管理器对象的 .__enter__(self) 方法。如果有 as 子句, .__enter__(self) 方法返回值赋值给 as 子句
中的变量 var ;如果没有 as 子句,则 .__enter__(self) 方法返回值直接丢弃。并不是将环境管理器对象赋值
给 var

执行 statements 代码块

如果 statements 代码块抛出异常,则 .__exit__(self, exc_type, exc_value, traceback) 方法自动被调用

在内部这几个实参由 sys.exc_info() 返回 (exc_type, exc_value, traceback) 信息,

若 .__exit__() 方法返回值为 False ,则重新抛出异常到 with 语句之外


若 .__exit__() 方法返回值为 True ,则异常终止于此,并不会抛出 with 语句之外

如果 statements 代码块未抛出异常,则 .__exit__(self, exc_type, exc_value, traceback) 方法自动被调用,调


用参数为: .__exit__(self,None,None,None)

本文档使用 书栈(BookStack.CN) 构建 - 107 -


29. 异常

9.Python3.1之后, with 语句可以指定多个环境管理器,以逗号分隔。根据定义的顺序这些环境管理器对象


的 .__enter__(self) 方法顺序调用, .__exit__(self, exc_type, exc_value, traceback) 方法逆序调用

如果对象要支持环境管理协议,则必须实现 .__enter__(self) 方法和 .__exit__(self, exc_type, exc_value, traceback) 方法

本文档使用 书栈(BookStack.CN) 构建 - 108 -


29. 异常

本文档使用 书栈(BookStack.CN) 构建 - 109 -


30. 异常对象

异常对象
1.Python3中,内置异常与用户自定义异常都是类的实例对象

2.在 try...except 语句进行 except ExceptionType 子句匹配时,采用的


是 isinstance(exception_obj,ExceptionType) 这种匹配规则。因此如果 ExceptionType 是 exception_obj 所属类的超
类,则匹配也成功。

3.Python中的内置异常类继承树:

1. graph BT
2. id1(OverflowError) -->|继承|id2(ArithmeticError)
3. id2 -->|继承|id3(Exception)

4. id4(IndexError) -->|继承|id3
5. id3-->|继承|id5(BaseException)

用户自定义异常类不要直接从 BaseException 继承。 BaseException 提供了默认的打印和状态保持行为


在构造时传给异常类的所有参数都将作为一个元组存储在 .args 属性中
在构造时传入的字符串作为 .__str(self)__ 方法返回。如果传入的不是字符串,
则将先调用 str() 将该参数转换为字符串

Exception 是所有内置异常类的超类。用户自定义的异常类都继承自它

系统退出事件 SystemExit 、 KeyboardInterrupt 、 GeneratorExit 不能继承自它

4.自定义异常类:通常继承自 Exception 类

若想自定义打印显示,则必须重写 .__str__(self) 方法

如果想定制初始化方法,必须重写 .__init__(self,args) 方法。此时超类的 .args 属性同样也会起作用

本文档使用 书栈(BookStack.CN) 构建 - 110 -


30. 异常对象

5.Python在运行时会将 try 语句放入堆栈中。抛出异常时,Python跳转至最近的 try 块中,找到匹配该异常的


异常处理器(即 except子句 ),执行异常处理的 except 子句。一旦异常被捕获并且处理,则其生命周期结束

异常的传递:向上返回到先前进入但是尚未离开的 try

6.Python中所有的错误都是异常。但是并非所有的异常都是错误

内置的 input 函数每次调用时,遇到文件末尾时引发内置的 EOFError

调用 sys.exit() 会触发 SystemExit 异常

在键盘上按下 Ctrl-C 键时,会触发 KeyboardInterrupt 异常

7.用户自定义的异常可以用于触发信号条件。这是利用异常来传递信息的方法

8. try...finally 通常用于释放系统资源。虽然垃圾收集时资源会自动释放,但是垃圾收集的时机不可控,由算法自
动调度

9.可以在顶层代码中使用 try 以及空的 except 来进行调试,从捕获程序有什么意外情况发生

10. sys.exc_info() 函数返回最近引发的异常信息,它返回一个三元素的元组: (type,value,traceback)

type :异常类型
value :异常实例对象

本文档使用 书栈(BookStack.CN) 构建 - 111 -


30. 异常对象

traceback :一个 traceback 对象,代表异常发生时所调用的堆栈

11.为了拦截具体的异常, except 应该具体化,避免拦截无关事件

空的 except 子句拦截任何异常,包括内存错误、系统推出、键盘中断等等
但是太具体化不利于扩展

本文档使用 书栈(BookStack.CN) 构建 - 112 -


31. Unicode与字节串

Unicode与字节串
1.字符编码:

ASCII 编码:每个字符存储在一个字节中。字符码值从 0~127


Latin-1 编码:每个字符存储在一个字节中。字符码值从 0~255。其中码值128~255分配给特殊字符;码值
0~127部分与
ASCII 编码相同。
UTF-8 编码:每个字符存储的字节数量可变,不再是固定编码
ASCII 编码是 UTF-8 编码的子集,也是 Latin-1 编码的子集

2. Unicode 文本通常叫做“宽字符”字符串,因为每个字符可能表示为多个字节

3.查看字符的 Unicode 码值: ord() 函数;


查看 Unicode 码值对应的字符: chr() 函数

这里的 Unicode 码值都是整数,可以是十进制、二进制、八进制、十六进制整数等

4.Python大约支持上百中不同的编码。可以导入 encodings 模块,并运行 help(encodings) 显示很多编码名称。有


一些编码是Python中实现的,一些是C中实现的。

有些编码对应多个不同的名称

本文档使用 书栈(BookStack.CN) 构建 - 113 -


31. Unicode与字节串

5.Python3中有三种字符串相关类型:

str 类型表示 Unicode 文本(8位和更宽的位数),为不可变的字符序列,称为字符串

bytes 表示二进制数据,称为字节串。 bytes 对象其实是小整数的一个序列,每个整数的范围在0~255之


间。

索引一个 bytes 实例返回一个整数


分片一个 bytes 实例返回一个新的 bytes 实例
list(bytes_obj) 返回一个整数列表而不是字符列表
bytes 类型几乎支持所有的 str 操作,但是不支持字符串格式化操作(没有字节串格式化操作)

bytearray 是一种可变的 bytes 类型,称为可变字节串。 bytearray 是 bytes 的一个变体,


它是可变的且支持原地修改。它支持 str 与 bytes 的常见操作,以及与列表相同的一些原地修改操作。

本文档使用 书栈(BookStack.CN) 构建 - 114 -


31. Unicode与字节串

6. sys.getdefaultencoding() 函数返回平台默认的编码方式。 sys.getfilesystemencoding() 返回系统文件的默认编码


方式。

7.Python3中,当一个文件以文本模式打开时,读取其数据会自动将其内容解码,并返回一个字符串;当一个文件以
文本模式写打开时,写入一个字符串会在将该字符串写入文件之前自动编码它。

本文档使用 书栈(BookStack.CN) 构建 - 115 -


31. Unicode与字节串

编码和解码的类型是系统的平台默认编码类型,或者你手动设定的编码类型
根据编码类型,Python自动处理文件起始处的字节标记序列(通常用于标记文件编码类型)
Python自动对行尾换行符转换。在windows下,换行符 \n 在写入文件时转换为windows下的换行符 \r\n 。
在读取文件时windows下的换行符 \r\n 转换为标准换行符 \n

当一个文件以二进制模式打开时,读取其数据直接返回其原生内容而并不以任何方式解码,也不做任何方式修改(即
不转换换行符),直接作为 bytes 实例返回;写入会接受一个 bytes 实例或者一个 bytearray 实例,并且不加修
改地写入到文件(即不转换换行符)

8.在Python3中, 'xxx' 、 "xxx" 、 '''xxx''' 均创建一个字符串常量,而添加一个 b 或者 B 中创建一


个 bytes 常量 b'xxx' 、 B"xxx" 、 b'''xxx'''

尽管 bytes 打印出来是字符串(若无对应的字符则输出内存表示),但它本质上是一个小整数序列

Python3中所有字符串都是Unicode字符(是ASCII字符的超集)

9.Python3中,虽然字符串与 bytes 的内存表示相同,但是二者不能混用,因为二者无法自动转换。对于期待一


个 str 实例作为参数的函数,它不能接受一个 bytes 实例;反之亦然

字符串的 .encode(encoding) 方法和 bytes 的 bytes(a_string,encoding) 函数将一个字符串实例转换为它原


生 bytes 形式
字符串的 str(a_bytes,encoding) 函数和 bytes 的 .decode(encoding) 方法将一个 bytes 实例解码成字符串形
式。

字符串的 .encode(encoding) 方法的 encoding 参数可以为空,此时表示使用平台默认编码

str(a_bytes) 函数返回的是 bytes 实例的打印字符串,而不是执行编码转换过程

本文档使用 书栈(BookStack.CN) 构建 - 116 -


31. Unicode与字节串

10.Python的字符串常量支持:

'\xNN' :单字节字符(8位),等价于 \u00NN

'\uNNNN' :双字节字符,16位

'\UNNNNNNNN' :4字节字符,32位

这里的 N 均为十六进制整数的一个整数位[0~F]

11.编解码ASCII字符非常简单,无需显示指定编解码类型(当然你可以随意指定一个编解码类型,因为ASCII编码是
任何编码类型的子集)

编解码非ASCII字符则要注意,对该字符的编码类型必须与解码类型一致,否则乱码或者抛出 UnicodeDecodeError 。

本文档使用 书栈(BookStack.CN) 构建 - 117 -


31. Unicode与字节串

12.生成Unicode字符串你可以通过Unicode转义序列来创建,如 'A\u4e2d\u56fd' ;也可以通过 chr() 函数来创


建,如
'A'+chr(0x4e2d)+chr(0x56fd) ,最终结果都是 'A中国'

13.字符串常量与 bytes 常量区别:

对字符串常量, '\xNN' 与 '\u00NN' 是等价的;对 bytes 常量, b'\xNN' 与 b'\u00NN' 是不等的


b'\xE8' 长度为2字节, b'\u00E8' 长度为6字节

字符串常量可以包含任意字符; bytes 常量要求字符要么是ASCII字符,要么是转义字符

len(string_literal) 得到字符串常量的字符个数; len(bytes_literal) 得到 bytes 常量的字节数

14.指定Python源文件字符集编码声明:在脚本的第一行写入:

1. # -*- coding: latin-1 -*-

15. bytes 实例的构造:

b'abc' :构造 bytes 常量


bytes('abc',encoding='ascii') :通过构造函数传入字符串和编码构造
bytes([97,98,99]) :通过传入小整数可迭代对象构造
'abc'.encode('ascii') :从字符串编码获取

16. bytearray 实例的构造:

bytearray('abc',encoding='ascii') :通过构造函数传入字符串和编码构造

bytearray(b'abc') :通过 bytes 常量构造

本文档使用 书栈(BookStack.CN) 构建 - 118 -


31. Unicode与字节串

17.打开文件时,可以通过 encoding 关键字参数指定打开文件的编码方式

18.Python的 struct 模块可以从字符串创建和提取打包的 bytes

19. pickle 模块存储 pickle 化的对象用的是 bytes

本文档使用 书栈(BookStack.CN) 构建 - 119 -


32. 管理属性

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

管理属性
1.管理属性的工具

.__getattr__(self,name) 方法:拦截所有未定义属性的读取(它要么返回一个值,要么抛出 AttributeError 异


常; .__setattr__(self,name,value) 方法:拦截所有属性的读取赋值(包括未定义的、已定义的)
.__getattribute__(self,name) 方法:拦截所有属性的读取(包括未定义的、已定义的)
property 特性:将特定属性访问定位到 get 方法和 set 方法
描述符协议:将特定属性访问定位到具有任意 get 和 set 方法的实例对象

2. property :每个 property 管理一个单一的、特定的属性。用法为:

1. class A:
2. def fget(...):
3. pass

4. def fset(...):
5. pass

6. def fdel(...):
7. pass

8. attribute=property(fget,fset,fdel,"doc") #必须在fget,fset,fdel之后定义
9. a=A()

10. a.attribute #调用的是property特性

property() 函数返回的是一个 property 对象

子类继承了超类的 property ,就和类的普通属性一样

本文档使用 书栈(BookStack.CN) 构建 - 120 -


32. 管理属性

3.描述符:描述符是作为独立的类创建,它的实例是赋值给了类属性

描述符的实例可以由子类继承
描述符的实例管理一个单一的特定的属性
从技术上讲, property() 创建的是一个描述符实例( property 实例)
描述符实例针对想要拦截的属性名访问操作,它提供了特定的方法

描述符类的接口为(即描述符协议):

1. class Descriptor:

2. '''
3. This is docstring
4. '''
5. def __get__(self,instance,owner):
6. pass

7. def __set__(self,instance,value):

8. pass
9. def __delete__(self,instance):
10. pass

11. class A:
12. attr=Descriptor()

13. ...

instance 参数为:

本文档使用 书栈(BookStack.CN) 构建 - 121 -


32. 管理属性

None :当用于类的属性访问时(如 cls.attr )


类 A 的实例对象:当用于实例的属性访问时(如 instance.attr )
owner 参数为:使用该描述符的类 A

当访问类实例或者类属性时,自动调用该类的描述符实例的方法。如果该类的描述符中某些方法空缺则:
若 __set__(self,instance,value) 未定义,则写该属性抛出 AttributeError ,该属性只读
若 __get__(self,instance,owner) 未定义,则读该属性返回一个 Descriptor 实例,
因为从继承树中可知,该属性返回由类的 attr 变量名指定的对象

状态信息可以保持在实例对象中,也可以保存在描述符实例中。因为在这3个方法中, self , instance 都可


以访问

4. .__delattr__(self,name) 方法拦截属性的删除

本文档使用 书栈(BookStack.CN) 构建 - 122 -


32. 管理属性

delattr(x,'name') 删除了 x.name 属性

5.由于 .__getattribute__(self,name) 方法和 .__setattr__(self,name,value) 方法对所有的属性拦截,因此他们的实


现特别要小心,注意不要触发无穷递归。

.__getattribute__(self,name) 方法中,若要取属性则可以用超类的 .__getattribute__(self,name) 获取。如果通


过 .__dict__ 方法获取则会再次触发 .__getattribute__(self,name) 的调用

.__setattr__(self,name,value) 方法中,若要设置属性可以用 self.__dict__[name]=value 的方法,或者用超类


的 .__setattr__(self,name,value) 方法

6.Python3中,所有使用内置操作隐式的获取方法名属性(如 print(x) 用到
了 .__str__(self) ), .__getattr__(self,name) 、 .__setattr__(self,name,value) 、
.__getattribute__(self,name) 方法都不会拦截,因为Python在类中查找这样的属性,完全忽略了在实例中查找

7.属性拦截优先级:

在读取属性方面, __getattribute__ 优先级最高;在写属性方面, __setattr__ 优先级最高;在删除属性方

本文档使用 书栈(BookStack.CN) 构建 - 123 -


32. 管理属性

面,
__del__ 优先级最高

如果没有 __getattribute__ , __setattr__ 与 __del__ ,则读写删属性取决于描述符( property 也是一


种特殊的描述符)。其中如果同一个属性指定了多个描述符,则后面的描述符覆盖前面的描述符

因为本质上 property 是一种 descriptor

本文档使用 书栈(BookStack.CN) 构建 - 124 -


32. 管理属性

__getattribute__ 与 __getattr__ 区别: __getattribute__ 在任何属性读取的时候拦截,


而 __getattr__ 只有在未定义属性读取的时候拦截(约定俗成地,它要么返回一个值,要么返
回 AttributeError )。其中若二者同时存在则 __getattribute__ 优先级较高

本文档使用 书栈(BookStack.CN) 构建 - 125 -


32. 管理属性

本文档使用 书栈(BookStack.CN) 构建 - 126 -


33. 装饰器

装饰器
1.装饰器是用于包装其他可调用对象的一个可调用对象,它是一个可调用对象,其调用参数为另一个可调用对象<,它
返回一个可调用对象

一个函数对象是可调用对象。
一个类对象是可调用对象,对它调用的结果就是返回类的实例

实现了 .__call__() 方法的类,其实例对象是可调用对象,对它调用的结果就是调用 .__call__() 方法

装饰器有两种使用形式:

函数的装饰器:在函数对象定义的时候使用装饰器,用于管理该函数对象
类的装饰器:在类定义的时候使用该装饰器,用于管理该类以及类的实例

装饰器是装饰器模式的一个实现

2.函数的装饰器:用于管理函数。函数的装饰器声明为:

1. @decorator
2. def func(*pargs,**kwargs):

3. pass

即在正常的函数定义之前冠以 @decorator 说明符(即装饰器声明)。它等价于:

1. def func(*pargs,**kwargs):

2. pass
3. func=decorator(func)

类中的 @staticmethod 、 @classmethod 、 @property 均为装饰器

本文档使用 书栈(BookStack.CN) 构建 - 127 -


33. 装饰器

执行了装饰器的 def 之后,函数名指向的不再是原来的函数对象,而是:

一个可调用对象, 当 decorator 是个函数时由 decorator(func) 函数返回的


decorator 类的实例,当 decorator 是个类时,由 decorator(func) 构造方法返回

3.类的装饰器:用于管理类。类的装饰器声明为:

1. @decorator

2. class A:
3. pass

即在正常的类定义之前冠以 @decorator 说明符(即装饰器声明)。它等价于:

1. class A:
2. pass

3. A=decorator(A)

类的装饰器并不是拦截创建实例的函数调用,而是返回一个不同的可调用对象

执行了装饰器的 class 之后,类名指向的不再是原来的类对象,而是:

一个可调用对象, 当 decorator 是个函数时由 decorator(func) 函数返回的


decorator 类的实例,当 decorator 是个类时,由 decorator(func) 构造方法返回

本文档使用 书栈(BookStack.CN) 构建 - 128 -


33. 装饰器

3.装饰器只是一个返回可调用对象的可调用对象,它没有什么特殊的地方。

可以用函数实现装饰器:

1. def decorator(func): #定义了一个叫decorator的装饰器

2. #某些处理
3. return func #返回可调用对象

也可以用类来实现装饰器:

1. class decorator:

2. def __init__(self,func):
3. self.func=func
4. def __call__(self,*args,**kwargs):

5. return self.func

通常用嵌套类来实现装饰器:

1. def decorator(func): #定义了一个叫decorator的装饰器

2. def wrapper(*args):
3. #使用func或其他的一些工作

本文档使用 书栈(BookStack.CN) 构建 - 129 -


33. 装饰器

4. return wrapper #返回可调用对象

4.装饰器的嵌套:

函数的装饰器的嵌套:

1. @decoratorA

2. @decoratorB

3. @decoratorC
4. def func():
5. pass

等价于

1. def f():
2. pass

3. f=A(B(C(f)))

本文档使用 书栈(BookStack.CN) 构建 - 130 -


33. 装饰器

类的装饰器的嵌套:

1. @decoratorA

2. @decoratorB
3. @decoratorC
4. class M:
5. pass

等价于

1. class M:

2. pass
3. M=A(B(C(M)))

每个装饰器处理前一个装饰器返回的结果,并返回一个可调用对象

5.装饰器可以携带参数。

函数定义的装饰器带参数:它其实是一个嵌套函数。
外层函数的参数为装饰器参数,返回一个函数(内层函数)
内层函数的参数为 func ,返回一个可调用参数,内层函数才是真正的装饰器

1. def decorator(*args,**kwargs):
2. print("this is decorator1:",args,kwargs)

3. def actualDecorator(func): # 这才是真实的装饰器


4. ...

5. return func
6. return actualDecorator

类定义的装饰器带参数:它其实是一个嵌套类。
外层类的初始化函数的参数为装饰器参数,外层类的 __call__ 函数的参数为 func ,返回值为一个类的
实例(内部类实例)
内层类的初始化函数参数为 func ;内层类的 __call__ 函数使用 func ,内层类才是真正的装饰器

1. class decorator2:

2. class ActualDecorator: #这才是真实的装饰器


3. def __init__(self,func):

本文档使用 书栈(BookStack.CN) 构建 - 131 -


33. 装饰器

4. ...
5. self.func=func#记住func
6. def __call__(self,*args,**kwargs):
7. ...

8. return self.func(*args,**kwargs) #使用func


9. def __init__(self,*args,**kwargs):
10. ...
11. def __call__(self,func):

12. ...
13. return decorator2.ActualDecorator(func)

总结:

不带参数的装饰器 decorator 装饰一个名字 F (可能为函数名、也可能为类名) @decorator :则执行的


是: F=decorator(F) ,直接使用 F

带参数的装饰器 decorator 装饰一个名字 F (可能为函数名、也可能为类名) @decorator(args) :则执行的


是: F=decorator(args)(F) ,间接使用 F

6.利用装饰器可以实现单例模式:

1. def Singleton(cls):
2. instance=None
3. def onCall(*args,**kwargs):

4. nonlocal instance
5. if instance == None:
6. instance=cls(*args,**kwargs)

7. return instance

8. return onCall
9. @Singleton
10. class A:

11. pass

本文档使用 书栈(BookStack.CN) 构建 - 132 -


33. 装饰器

7.利用装饰器可以跟踪对象的调用接口,从而管理对实例的接口访问(如统计调用次数,打印调用日志)

1. def Tracer(cls):
2. class Wrapper:

3. def __init__(self,*args,**kwargs):
4. self.wrapped=cls(*args,**kwargs)
5. def __getattr__(self,name):
6. print('Trace:'+name)

7. return getattr(self.wrapped,name)

8. return Wrapper
9. @Tracer

10. class A:
11. pass

本文档使用 书栈(BookStack.CN) 构建 - 133 -


33. 装饰器

8.装饰器也可以直接管理函数和类,而不仅仅只是管理对他们的调用

利用装饰器添加函数和类到注册表:

1. register_dict={}

2. def register(obj):
3. register_dict[obj.__name__]=obj
4. return obj
5. @register

6. def func():
7. pass

利用装饰器为函数和类添加属性

1. def register(obj):
2. obj.label=0

3. return obj
4. @register
5. def func():

6. pass

本文档使用 书栈(BookStack.CN) 构建 - 134 -


34. 元类

元类
1.元类是一种特殊的类,它用于创建类。元类机制允许我们在一条 class 语句的末尾自动插入某些逻辑。它在类对
象创建时运行,是管理和扩展类的钩子。元类不是管理类的实例,而是管理类本身

2.尽管类的装饰器通常用来管理或者扩展类实例,但是他们也可以用于管理和扩展类对象本身,也与元类的功能重叠

3.Python3中,所有用户定义的类都是 type 类对象的实例, type 类是应用最广的元类

4. class 语句的内部机制:在一条 class 语句的末尾,Python会调用 type 类的构造函数来创建一


个 class 对象。

1. MyClass=type(classname,superclasses,attributedict) #新建了一个类,类名叫MyClass
2. # classname:类名,会成为MyClass类的 .__name__属性
3. # superclasses:类的超类元组,会成为MyClass类的 .__bases__属性

4. # attributedict:类的命名空间字典,会成为MyClass类的 .__dict__ 属性
5. # 这个语句也是动态创建类对象的方法

type 类定义了一个 .__call__(...) 方法。该方法运行 type 类定义的两个其他方法:


.__new__(mclass,classname,superclasses,attributedict) 方法,它返回新建的 MyClass 类
mclass :为本元类,这里是 type 类
classname :为被创建的类的类名,这里是 'MyClass'
superclasses :为被创建的类的超类元组
attributedict :为被创建的类的名字空间字典

.__init__(customclass,classname,superclasses,attributedict) 方法,
它初始化新建的 MyClass 类
customclass :为被创建的类,这里是 MyClass 类
classname :为被创建的类的类名,这里是 'MyClass'
superclasses :为被创建的类的超类元组
attributedict :为被创建的类的名字空间字典

5.所有的类型均由 type 类创建。要通知Python用一个定制的元类来创建类,可以直接声明一个元类来拦截常规的


类创建过程。

定义元类:(所有元类必须是 type 的子类)

1. class MetaClass(type):
2. def __new__(mclass,classname,superclasses,attributedict):

3. return type.__new__(mclass,classname,superclasses,attributedict)
4. def __init__(customclass,classname,superclasses,attributedict):

5. return type.__init__(customclass,classname,superclasses,attributedict)

使用元类:

1. class MyClass(metaclass=MetaClass):

2. pass

继承的超类也列在括号中,但是要在元类之前,也用逗号分隔:

本文档使用 书栈(BookStack.CN) 构建 - 135 -


34. 元类

class MyClass(BaseCls1,BaseCls2,metaclass=MetaClass)

使用元类声明后,在 class 语句底部进行创建 MyClass 类时,改为调用元类 MetaClass 而不是默认的 type :


MyClass=Meta('MyClass ,superclasses,attributedict)`

元类 MetaClass 要实现元类协议:

重载元类的 .__new__(Meta,classname,superclasses,attributedict) 方法,它返回新建的 MyClass 类


重载元类的 .__init__(customclass,classname,superclasses,attributedict) 方法,
它初始化新建的 MyClass 类
type 类的 .__call__(...) 方法将创建和初始化 MyClass 类对象的调用委托给元类MetaClass`

6.元类有的时候不一定是个真正的类,它也可能是一个函数。任何可调用对象都可以作为一个元类,只需要按照以下
的做法:

1. def MetaFactory(classname,superclasses,attributedict):
2. ...

3. return type(classname,superclasses,attributedict) #动态创建类型


4. class A(metaclass=MetaFactory):

5. pass

在 class 语句的末尾会调用 MetaFactory 函数

7.事实上元类只用于创建类对象,元类并不产生元类自己的实例。因此元类的名字查找规则有些不
同: .__call__ , .__new__ , .__init__ 方法均在类中查找

8.元类的继承:

本文档使用 书栈(BookStack.CN) 构建 - 136 -


34. 元类

元类声明由子类继承,即子类的构建也是由父类的元类负责

如果元类是以函数的方式声明,则子类的构建不再继承这个函数式元类

元类中的属性并不进入自定义类的命名空间,即元类中声明的一些类属性与被创建类的名字空间无关(他们是两
个不同的类)

自定义的类,如果没有显示指定元类,也没有指定父类,则默认使用 type 作为元类(即常规的类创建机制)

本文档使用 书栈(BookStack.CN) 构建 - 137 -


35. Python 执行细节

1. 作者:华校专

2. email: [email protected]
3. ** 本文档可用于个人学习目的,不得用于商业目的 **

Python 执行细节
1.Python脚本执行时,Python内部会首先将源代码编译成字节码的形式。字节码是平台无关的。

字节码是平台无关的
如果Python进程拥有写入权限,则它会将脚本的字节码以一个 .pyc 为扩展名的文件。当脚本运行后你可以在
源代码所在的目录附近看到 .pyc 文件。
下一次运行脚本时,如果你在上次保存字节码之后没有修改源代码时,Python会自动加载 .pyc 文件并
跳过编译步骤
如果Python进程没有写入权限,则字节码会在内存中生成并在脚本执行结束后抛弃。
.pyc 文件也是发布Python程序的方法之一。此时并不需要提供 .py 源代码。

2.字节码由Python虚拟机(简称 PVM )来解释执行。实际上 PVM 不是一个独立的程序,它只是迭代运行字节码指


令的一个大循环而已,它是Python系统的一部分。

本文档使用 书栈(BookStack.CN) 构建 - 138 -

You might also like