Python 官方教程
前言
- 这是一次系统学习Python官方教程的学习笔记
- 整个教程一共16章, 在学习过程中记录自己不知道的和一些重要的知识, 水平有限, 请指正.
- Python3.7 官方教程.
Python的特点
- 提供高效的高级数据结构, 有效的面向对象编程.
- 第三方Python模块, 程序和工具.
- Python解释器易于扩展, 可使用C或C++扩展新的功能和数据结构.
- Python优雅的语法和动态类型, 以及解释型语言本质, 使它成为多数平台上写脚本和快速开发应用的理想语言.
Python官方库的其他链接
Python官方教程的学习之旅
课前甜点
- shell脚本最擅长移动文件和替换文本,并不适合GUI界面或者游戏开发。
- Python 允许你划分程序模块,在其他的 Python 程序中重用。
- Python是一种解释型语言,在程序开发阶段可以为你节省大量时间,因为不需要编译和链接。
- 高级数据类型允许在一个表达式中表示复杂的操作;
- 代码块的划分是按照缩进而不是成对的花括号;
- 不需要预先定义变量或参数。
- Monty Python的飞行马戏团.
使用 Python 解释器
- Python解释器通常放在
/usr/local/bin/python3.7
, 我的电脑上解释器位于/usr/bin/python3
. -
python -m moudule [arg] ...
会执行module的源文件, 跟在命令行把路径写全一样.
传入参数
- 解释器会读取命令行参数, 转化为字符串列表存入
sys
模块中的argv
变量, 像C语言main函数的参数入口int main(int argc, char* argv[])
的命令行参数一样的道理.-
import sys
可以导入sys
这个模块并访问这个列表.
-
解释器的运行环境
- 默认情况下, Python源码文件以
UTF-8
编码方式处理, 也可以通过# -*- coding: encoding -*-
来修改字符编码 - 第一行规则的一种例外情况:
- 源码以UNIX "shebang" 行开头.
- 脚本的第一行一般都为
#!/usr/bin/env python3
. - 在Unix系统中,Python 3.x解释器默认安装后的执行文件并不叫作 python,这样才不会与同时安装的Python 2.x冲突。
Python的非正式介绍
- Python中的注释以井号
#
开头, 延伸到该行结束. - Python在做除法时,
/
一般会保留小数位,//
会忽略掉小数点后的所有小数部分. -
**
来计算乘方. - Python中提供浮点数的完整支持;包含多种混合类型运算数的运算会把整数转换为浮点数.
- Python也内置对复数的支持, 使用后缀
j
或J
就可以表示虚数部分(3+5j
). - Python中字符串有多种形式, 单引号和双引号都可以获得同样的效果. 特殊字符会使用反斜杠
\
来转义.- print() 函数会生成可读性更强的输出,即略去两边的引号,并且打印出经过转义的特殊字符.
- 不希望前置的反斜杠
\
转义成特殊字符, 可以使用原始字符串方式, 在引号前添加r
即可. - 字符串跨行连续输入, 三重引号
"""..."""
或'''...'''
. - 字符串可以用
+
进行连接, 也可以使用*
进行重复. - 字符串是可以被索引(下标访问), 第一个字符索引是0, 索引也可以是负数, 会从右边开始.
-
-0
和0
是一样的. - 切片可以获得子字符串. 切片索引的默认值为0, 省略结束索引为字符串的结束.
-
- 字符串是一种序列类型, 因此也支持序列类型的各种操作.
- 字符串支持许多变换和查找的方法.
- 使用
str.format()
进行字符串格式化.
列表
- Python中可以组合一些值得到多种复合数据类型. 列表通过方括号括起, 逗号分隔的一组值得到.
- 和字符串及内建的序列类型(sequence)一样, 列表也支持索引和切片.
- 所有切片都返回一个新列表, 这个新列表包含所需要的元素(浅拷贝).
- 字符串是不可修改的(常量的, immutable), 列表是一个可修改的(mutable)类型.
- 列表通过
append()
方法添加新元素. 给切片赋值也是可以的. - 内置函数
len()
也可以作用到列表上. - 嵌套列表, 列表的列表.
- 多重赋值, 多个变量可以同时通过一个等号进行赋值, 例如
a, b = 0, 1
. - 循环体缩进, 通过TAB键或者多个空格.
- print()函数可以将所有传进来的参数打印出来, 包括浮点数, 字符串, 并且在参数项之间会插入一个空格.
-
**
比-
具有更高的优先级. - 不必在单引号里转移双引号.
其他流程控制工具
- if语句可以有零个或多个
elif
部分, 以及一个可选的else
部分. - Python中的for语句并不总是对算术递增的数值进行迭代, 或是给予用户定义迭代步骤和暂停条件的能力, 而是对任意序列进行迭代(列表或字符串), 条目的迭代顺序与它们在序列中出现的顺序一致.
-
for w in words[:]
.
-
- range()函数这个内置函数会遍历一个数列, range(10) 会生成10个值,并且是以合法的索引生成一个长度为10的序列.
- range(0, 10, 3)的输出为
0, 3, 6, 9
. - range(-10, -100, -30)的输出为
-10, -40, -70
. - range()并没有真正生成一个列表.
- range(0, 10, 3)的输出为
- 以序列的索引来进行迭代:
for i in range(len(a))
. - break和continue语句:
- break将跳出最近的for或while循环.
- else子句会在循环条件或者if条件为假的时候执行, 但是不会在循环被break终止时执行.
- try语句中的else语句有相同的效果.
- else 子句会在未发生异常时执行.
- pass语句什么也不会做, 可作为一个函数或条件子句体的占位符.
定义函数
- 利用关键字
def
引入一个函数定义, 它必须后跟函数名称和带括号的形式参数列表, 构成函数体的语句从下一行开始, 并且必须缩进. - 函数中的所有变量赋值都将值存储在本地符号表中.
- 变量引用首先在本地符号表中查找,然后在封闭函数的本地符号表中查找,然后在全局符号表中查找,最后在内置符号表中查找.
- 全局变量不能直接在函数中赋值(除非使用 global 命名).
- 在函数被调用时,实际参数(实参)会被引入被调用函数的本地符号表中.
- 实参是通过 按值调用 传递的(其中 值 始终是对象 引用 而不是对象的值).
- 当一个函数调用另外一个函数时,将会为该调用创建一个新的本地符号表.
def fib(n): # write Fibonacci series up to n
"""Print a Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
- 返回一个None内置名称.
- return语句会从函数内部返回一个值, 不带表达式参数的 return 会返回 None, 函数执行完毕退出也会返回 None.
- 参数默认值, 最有用的形式是对一个或多个参数指定一个默认值
def ask_ok(prompt, retries=4, reminder='Please try again!'):
.- 默认值只会执行一次.
- 可以使用形如 kwarg=value 的 关键字参数 来调用函数.
任意的参数列表
- 使用任意数量的参数调用函数, 这些参数会被包含在一个元祖中, 在可变参数之前可能会出现零个或多个普通参数.
-
def write_multiple_items(file, separator, *args):
. - *args 参数之后的任何形式参数都是 ‘仅关键字参数’,也就是说它们只能作为关键字参数而不能是位置参数.
-
- 解包参数列表:
- 使用 * 运算符编写函数调用以从列表或元组中解包参数.
- 字典可以使用 ** 运算符来提供关键字参数.
Lambda表达式
- 可以用 lambda 关键字来创建一个小的匿名函数.
-
lambda a, b: a+b
. - lambda函数可以在需要对象的任何地方使用.
- 在语法上仅限于单个表达式, lambda函数可以引用包含范围的变量.
-
- 文档字符串:
- 第一行应该是对象目的的简要概述;
- 如果文档字符串中有很多行, 则第二行应为空白.
- Python解析器不会从Python中删除多行字符串文字的缩进,因此处理文档的工具必须在需要时删除缩进.
- 函数标注:
- 函数标注, 是关于用户自定义函数中使用的类型的完全可选元数据信息.
- 函数标注, 以字典的形式存放在函数的 annotations 属性中,并且不会影响函数的任何其他部分.
代码风格
- 使用4个空格缩进,不要使用制表符。
- 换行,使一行不超过79个字符。
- 使用空行分隔函数和类,以及函数内的较大的代码块。
- 如果可能,把注释放到单独的一行。
- 使用文档字符串。
- 在运算符前后和逗号后使用空格,但不能直接在括号内使用: a = f(1, 2) + g(3, 4)。
类和函数命名的一致性;规范是使用CamelCase命名类, lower_case_with_underscores命名函数和方法。始终使用 self 作为第一个方法参数的名称.
通过对象引用调用会是一个更好的表述, 因为如果传递的是可变对象, 则调用者将看到被调用者对其做出的任何更改(插入到列表中的元素).
数据结构
- 列表的更多特性(列表类型还有很多的方法):
- list.append(x) --- 在列表末尾添加一个元素.
- list.extend(iterable) --- 使用可迭代对象中的所有元素来扩展列表.
- list.insert(i, x) --- 在给定的位置插入一个元素.
- list.remove(x) --- 移除列表中第一个值为 x 的元素.
- list.pop(i) 或 list.pop() --- 删除列表中给定位置的元素并返回它.
- list.popleft() --- 删除列表最前面的元素.
- list.clear() --- 删除所有的元素, 相当于del a[:].
- list.index(x[, start[, end]]) --- 返回列表中第一个值为 x 的元素的从零开始的索引.
- []是可选参数.
- list.count(x) --- 返回元素x在列表中出现的次数.
- list.sort(key=None, reverse=False) --- 对列表中的元素进行排序, 可自定义排序.
- list.reverse() 反转列表中的元素.
- list.copy() 返回列表中的一个浅拷贝. 相当于
a[:]
.
- 列表可以作为栈使用, 也可以作为队列使用.
- 列表推导式:
- 列表推导式提供了一个更简单的创建列表的方法;
- 常见的用法是把某种操作应用于序列或可迭代对象的每个元素上,然后使用其结果来创建列表,或者通过满足某些特定条件元素来创建子序列.
- 列表推导式的结构是由一对方括号:
- 一个表达式;
- 后面跟一个 for 子句,然后是零个或多个 for 或 if 子句;
- 其结果将是一个新列表.
- 如果表达式是一个元组, 那么就必须加上括号.
- 嵌套的列表推导:
- 列表推导式中的初始表达式可以是任何表达式,包括另一个列表推导式.
-
zip()
函数将会很好地处理复杂的流程语句.
- del语句:
- 从列表按照给定的索引而不是值来移除一个元素.
元组和序列
- 一个元组(Tuples)由几个被逗号隔开的值组成.
- 元组是不可修改的(不可变的, immutable).
- 元组在输出时总是被圆括号包围的,以便正确表示嵌套元组; 但输入时圆括号可有可无.
- 空元组可以直接被一对空圆括号创建, 含有一个元素的元组可以通过这个元素添加一个逗号来构建.
- 这被称为 序列解包 也是很恰当的,因为解包操作的等号右侧可以是任何序列。
- 序列解包要求等号左侧的变量数与右侧序列里所含的元素数相同。
- 注意可变参数其实也只是元组打包和序列解包的组合.
- 集合: 集合是由不重复元素组成的无序的集, 包括成员检测和消除重复元素.
- 集合也支持像联合, 交集, 差集, 对称差分等数学运算.
*花括号{}
或set()函数可以创建集合, 但创建一个空集合只能用set()而不能用{}
, 因为默认{}
是创建一个空字典. -
a | b
,a & b
,a ^ b
. - 集合也支持列表推导式.
- 集合也支持像联合, 交集, 差集, 对称差分等数学运算.
字典
- 字典(映射类型, dict), 在其他语言里可能被叫做联合内存或联合数组.
- 字典以关键字为索引值, 关键字可以是任意不可变类型, 通常是字符串或者数字.
- 如果一个元组只包含字符串、数字或元组,那么这个元组也可以用作关键字.
- 但如果元组直接或间接地包含了可变对象,那么它就不能用作关键字.
- 列表不能用作关键字,因为列表可以通过索引、切片或 append() 和 extend() 之类的方法来改变.
- 将字典可以理解为键:值对的集合, 其中减必须是唯一的(在一个字典中), 一对花括号可以创建一个空字典
{}
. - 字典主要的操作是使用关键字存储和解析值, 也可以用del删除一个键值对.
- 如果使用了一个已经存在的关键字来存储值, 那么之间与这个关键字关联的值就会被遗忘, 用一个不存在的键来取值则会报错.
- 一个字典执行
list(d)
将返回包含该字典中所有键的列表. - 按插入次序排列,
sorted(d)
. - 检查字典中是否存在一个特定键, 可使用in关键字.
- dict()构造函数可以直接从键值对序列中创建字典.
- 字典推导式可以从任意的键值表达式中创建字典.
当关键字是简单字符串时,有时直接通过关键字参数来指定键值对更方便.
- 循环的技巧:
- 当在字典中循环时, 用items()方法可将关键字和对应的值同时取出.
- 当在序列中循环时, 用enumeration()函数可以将索引位置和其对应的值同时取出.
- 当同时在两个或更多序列中循环时, 可以用zip()函数将其内元素一一匹配.
- 当逆向循环一个序列时, 先正向定位序列, 然后调用reversed()函数.
- sorted()函数在不改动原序列的基础上返回一个新的排好序的序列.
- 有时可以在循环时修改列表内容, 一般来说改为创建一个新列表是比较简单且安全的.
- 深入条件控制:
- 比较操作符 in 和 not in 校验一个值是否在(或不在)一个序列里.
- is 和 is not 操作符比较两个对象是不是同一个对象, 这只跟像列表这样的可变对象有关.
- 所有的比较操作符都有相同的优先级, 且这个优先级比数值运算符低.
- 比较操作是可以传递的,
a < b == c
会校验是否a小于b并且b等于c. - 比较操作可以通过布尔运算符 and 和 or 来组合,并且比较操作(或其他任何布尔运算)的结果都可以用 not 来取反.
- not 优先级最高, or 优先级最低.
- Python中赋值操作不能发生在表达式内部.
- 序列和其他类型的比较:
- 序列对象可以与相同类型的其他对象进行比较.
- 使用字典顺序进行比较, 首先比较两个序列的第一个元素, 如果不同, 就决定比较结果; 如果相同, 就再比较序列的第二个元素, 直到一个序列耗尽.
- 对不同类型对象来说,只要待比较对象提供了合适的比较方法,就可以使用 < 和 > 来比较.
模块
- 如果从Python解释器中退出再次进入, 之前的定义(函数和变量)都会丢失.
- Python有一种方法可以把定义放在一个文件里, 并在脚本或解释器的交互式实例中使用它们, 这样的文件被称作模块.
- 模块中可以导入其他模块.
- 主模块 --- 在*和计算器模式下执行的脚本可以访问的变量集合.
- 模块是一个包含Python定义和语句的文件, 文件名以
.py
为后缀, 在一个模块内部, 通过__name__
全局变量获得模块名. - 模块可以包含可执行的语句以及函数定义.
- 这些语句用于初始化模块, 它们仅在第一次
import
语句中被导入时才被执行(当文件当作脚本运行时, 也会执行). - 每个模块都有自己的私有符号表, 用作模块中定义的所有函数的全局符号表.
- 可在模块内使用全局变量, 而不必担心和其他全局变量发生意外冲突.
- 可以用跟访问模块内的函数的同样标记方法, 去访问一个模块的全局变量, modname.itemname.
- 模块可以导入其他模块, 用
import
语句放在模块的开头, 被导入的模块名存放在调入模块的全局符号表中. - import语句有一个变体, 它可以把名字从一个被调模块内直接导入现在模块的符号表里.
-
from fibo import *
导入模块内定义的所有名称.
- 这些语句用于初始化模块, 它们仅在第一次
- 模块名称之后带有
as
, 则跟在as
之后的名称将直接绑定到所导入的模块. - 出于效率的考虑,每个模块在每个解释器会话中只被导入一次.
-
importlib.reload(modulename)
重新装载模块名.
-
- Python脚本执行过程中,
__name__
被赋值为__main__
, 模块的末尾一般加入以下代码:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
- 这样即可以把一个Python文件当作一个可调入的模块来使用, 又可以当作脚本使用.
- 解析命令行的代码只有在当模块是以
main
文件的方式执行的时候才会运行. 如果模块是被导入的, 那些代码是不运行的. -
import fibo
经常用于为模块提供一个方便的用户接口, 或用于测试.
- 解析命令行的代码只有在当模块是以
- 模块搜索路径:
- 解释器首先寻找具有该名称的内置模块, 如果没有找到, 然后从
sys.path
变量的目录列表中寻找名为spam.py
的文件. -
sys.path
初始有这些目录:- 包含输入脚本的目录(或者未指定文件时的当前目录).
-
PYTHONPATH
(一个包含目录名称的列表, 它和shell变量PATH有一样的语法). - 取决于安装默认设置.
- 在支持符号链接的文件系统上, 包含输入脚本的目录是在追加符号链接后才计算出来的.
- 包含符号链接的目录并没有被添加到模块的搜索路劲上.
- 在初始化后, Python程序可以更改
sys.path
, 包含正在运行脚本的文件目录被放在搜索路径的开头处.
- 解释器首先寻找具有该名称的内置模块, 如果没有找到, 然后从
- 为了加速模块载入, Python在
__pycache__
目录里缓存了每个模块的编译后版本, 被称为module.version.pyc
, 其中版本字段对编译文件的格式进行编码.- 一般使用Python版本号, spam.py的编译版本将被缓存为
__pycache__/spam.cpython-33.pyc
.
- 一般使用Python版本号, spam.py的编译版本将被缓存为
- 编译的模块与平台无关, 因此可以在具有不同体系结构的系统之间共享相同的库.
- Python在两种情况下不会检查缓存:
- 对于从命令行直接载入的模块, 它从来都是重新编译并且不存储编译结果.
- 如果没有源模块, 就不会检查缓存.
- 在Python命令中使用
-0
或者-00
, 以减小模块编译后的大小.-
-0
去除断言语句. -
-00
同时去除断言语句和__doc__
字符串. - 优化过的模块一般会有一个
opt-
的标签并且通常小些.
-
- 从一个
.pyc
文件读出的程序并不会比从.py
读出时运行更快,.pyc
文件唯一快的地方在于载入速度. -
compileall
模块可以为一个目录下的所有模块创建一个.pyc
文件. - Python具有一个标准模块库, 内置与解释器中, 他们提供不属于语言核心但仍然内置的操作访问.
- 可以提高效率或提供对系统调用等操作系统原语的访问.
- 这些模块的集合是一个配置选项, 也取决于底层平台.
-
sys
模块被内嵌到每个Python解释器中, 变量sys.ps1
和sys.ps2
定义用作主要和辅助提示的字符串.
-
sys.path
变量是一个字符串列表, 用于确定解释器的模块搜索路径.- 该变量被初始化为从环境变量
PYTHONPATH
获取的默认路径, 或者如果PYTHONPATH
未设置, 则从内置默认路径初始化, 也可以使用列表操作进行修改.
import sys sys.path.append('/ufs/guido/lib/python')
- 该变量被初始化为从环境变量
- dir()函数:
- 内置函数
dir()
用于查找模块定义的名称, 返回一个排序过的字符串列表. - 如果没有参数,
dir()
会列出当前定义的名称, 主要包括变量, 模块, 函数等名称. -
dir()
不会列出内置函数和变量的名称, 但标准模块builtins
中的可以.
- 内置函数
包
- 包是一种通过甩带点号的模块名来构造Python模块命名空间的方法.
- 一个包可以理解为一个模块的集合.
-
__init__.py
初始化一个包中的原始脚本文件. - 当导入一个包时, Python搜索
sys.path
中的目录, 查找包的子目录. - 为了将目录当作包, 目录中必须包含一个
__init__.py
文件, 这样做是为了防止具有通用名称的目录隐藏有效模块.-
__init__.py
为一个空文件即可, 也可以执行包的初始化代码, 或者定义一个__all__
变量.
-
- 包的用户可以从包中导入单个模块, 但引用时必须使用全名.
- 当使用
from package import item
时,item
可以是包的子模块(或子包),也可以是包中定义的其他名称,如函数,类或变量.- 没有对应的变量, 会产生
ImportError
异常.
- 没有对应的变量, 会产生
- 为包提供一个包的显式索引.
- 如果一个包的
__init__.py
代码定义了一个名为__all__
的列表,它会被视为在遇到from package import *
时应该导入的模块名列表.-
__all__ = ["echo", "surround", "reverse"]
. -
from sound.effects import *
将导入 sound 包的三个命名子模块.
-
使用
from Package import specific_submodule
没有任何问题.- 子包参考:
- 当包被构造成子包时(与示例中的 sound 包一样),你可以使用绝对导入来引用兄弟包的子模块.
- 可以使用import语句的
from module import name
形式编写相对导入. - 使用前导点来指示相对导入中涉及的当前包和父包.
- 相对导入是基于当前模块的名称进行导入的.
- 由于主模块的名称总是
__main__
, 因此用作Python应用程序主模块的模块必须始终使用绝对导入.
- 多个目录中的包:
- 包支持另一个特殊属性:
__path__
. - 它被初始化为一个列表, 其中包含在执行该文件中的代码之前保存包的文件
__init__.py
的目录的名称. - 这个变量可以修改, 这样做会影响将来对包中包含的模块和子包的搜索.
- 通常不需要此功能, 但它可用于扩展程序包中的模块集.
- 包支持另一个特殊属性:
函数定义也是被执行的语句, 模块级函数定义的执行在模块的全局符号表中输入该函数名.
输入输出
- 写入值的方式: 表达式语句和
print()
函数. - 使用文件对象的
write()
方法. - 标准输出文件可以作为
sys.stdout
引用. - 使用格式字字符串字面值: 在字符串的开始引号或三引号之前加上一个
f
或F
.- 在
{
和}
字符之间可以引用的变量或字面值的Python表达式.
- 在
- 字符串的
str.format()
方法需要更多的手动操作. - 可以使用字符串切片和连接操作自己完成所有的字符串处理,以创建你可以想象的任何布局.
- 使用
repr()
或str()
函数将任何值转换为字符串.-
str()
函数是用于返回人类可读的值表示. -
repr()
是用于生成器解释器可读的表示, 但两者将返回一样的值. -
string
模块包含一个Template
类, 它提供了另一种将值替换为字符串的方法, 使用类似$x
的占位符并用字典中的值替换它们, 但对格式的控制要少很多.
-
- 格式化字符串文字:
- 格式化字符串字面值 (常简称为 f-字符串)能让你在字符串前加上
f
和F
并将表达式写成 {expression} 来在字符串中包含 Python 表达式的值. - 在
':'
后传递一个整数可以让该字段成为最小字符宽度. -
'!a'
应用 ascii(),'!s'
应用 str(), 还有'!r'
应用 repr().
- 格式化字符串字面值 (常简称为 f-字符串)能让你在字符串前加上
- 字符串的format()方法:
- 花括号和其中的字符(称为格式字段)将替换为传递给 str.format() 方法的对象.
- 花括号中的数字可用来表示传递给 str.format() 方法的对象的位置.
- 如果在 str.format() 方法中使用关键字参数, 则使用参数的名称引用它们的值.
- 内置函数 vars() 结合使用时非常有用,它会返回包含所有局部变量的字典.
- 字符串对象的
str.rjust()
方法通过在左侧填充空格来对给定宽度的字段中的字符串进行右对齐. -
str.ljust()
左对齐,str.center()
中间对齐.str.zfill()
会在数字字符串的左边填充零. -
%
操作符也可以用作字符串格式化。它将左边的参数解释为一个很像sprintf()
风格的格式字符串,应用到右边的参数,并返回一个由此格式化操作产生的字符串.
读写文件
-
open()
返回一个文件对象, 最常用的有两个参数:open(filename, mode)
.- mode可以为
r
,w
,a
,r+
.-
r
--- 表示文件只能读取. -
w
--- 表示文件只能写入(已存在的同名文件会被删除). -
a
--- 表示打开文件以追加内容. - 任何写入的数据会自动添加到文件的末尾.
-
r+
表示打开文件进行读写. - mode参数是可选的, 省略是默认是
r
. - 在mode 中追加的
b
则以 binary mode 打开文件. 一般数据是以字节对象的形式进行读写的.
-
- mode可以为
- 默认会把平台特定的行结束符 (Unix上的
\n
, Windows上的\r\n
)统一转换为\n
. - 读取
JPEG
和exe
格式的文件时, 应使用二进制模式. - 在处理文件对象时,最好使用
with
关键字。 优点是当子句体结束后文件会正确关闭,即使在某个时刻引发了异常.-
with
相比等效的try-finally
代码块要简洁得多. 可以不用显示的关闭文件. - 如果没有使用
with
关键字, 应调用f.close()
关闭文件并理解释放它使用的所有系统资源. - 如果没有显示地关闭文件, Python的垃圾回收器最终将销毁该对象并为你关闭打开的文件,但这个文件可能会保持打开状态一段时间.
- 不同的Python实现会在不同的时间进行清理。
-
- 一般文件对象以
f
表示.- 读取文件的内容,
f.read(size)
--- 会读取一些数据并将其作为字符串(在文本模式下)或字节对象(在二进制模式下)返回. - size是一个可选的数字参数, 当size被省略或者为负时, 将读取并返回文件的整个内容.
- 如果文件的大小是机器内存的两倍, 那么就可能出现问题, 否则最多读取并返回size字节的内容.
- 如果已经到达文件末尾,
f.read()
将返回一个空字符串. -
f.readline()
从文件中读取一行, 换行符\n
留在字符串末尾, 如果文件不以换行符结尾, 则在文件的最后一行省略. - 如果
f.readline()
返回一个空的字符串, 则表示已经到达了文件末尾. - 以列表的形式读取所有行
list(f)
或f.readlines()
. -
f.write(string)
会把string的内容写入到文件中, 并返回写入的字符数. -
f.tell()
返回一个整数, 给出文件对象在文件中的当前位置, 表示为二进制模式下时从文件开始的字节数, 以及文本模式下的不透明数字. -
f.seek(offset, from_what)
--- 改变文件对象的位置, 通过向参考点添加offset来计算位置, 参考点由from_what参数指定.-
from_what
值为0时, 表示从文件开头开始. -
from_what
值为1时, 表示从当前位置开始. -
from_what
值为2时, 表示把文件末尾作为参考点.
-
- 在文本文件(那些在模式字符串中没有
b
的打开的文件)中,只允许相对于文件开头搜索使用 seek(0, 2) 搜索到文件末尾是个例外)并且唯一有效的 offset 值是那些能从 f.tell() 中返回的或者是零。其他 offset 值都会产生未定义的行为.
- 读取文件的内容,
- 使用json保存结构化数据:
- 字符串可以轻松地写入文件并从文件中读取出来.
-
read()
方法只能返回字符串. 这些字符串必须传递给类似int()
的函数, 他会接受类似'123'
这样的字符串并返回其数字值123. - Python允许使用JSON(JavaScript Object Notation)的流行数据交换格式, 而不是让用户不断的编写和调试代码以将复杂的数据类型保存到文件中.
- 名为
json
的标准模块可以采用Python数据层次结构, 并将它们转化为字符串表示形式.-
serializing
序列化. -
deserializing
反序列化. - 在序列化和反序列化之间, 表示对象的字符串可能已存储在文件或数据中, 或通过网络链接发送到某个远程机器.
- JSON格式通常被现代应用程序用于允许数据交换.
import json json.dumps([1, 'simple', 'list'])
-
- 将对象序列化为一个文本文件(text file).
json.dump(x, f)
. - 再次解码对象,
x = json.load(f)
. - 这种简单的序列化技术可以处理列表和字典,但是在JSON中序列化任意类的实例需要额外的努力.
错误和异常
- 语法错误又称解析错误,可能是你在学习Python 时最容易遇到的错误.
- 错误是由箭头指示的位置 上面 的 token 引起的(或者至少是在这里被检测出的).
异常
- 在执行时检测到的错误被称为异常,异常不一定会导致严重后果, 但大多数异常并不会被程序处理.
-
ZeroDivisionError
异常. -
NameError
异常. -
TypeError
异常. - 作为异常类型打印的字符串是发生的内置异常的名称.
-
-
try
语句的工作原理:- 首先, 执行
try
子句(try
和except
关键字之间(多行)语句). - 如果没有异常发生, 则跳过
except
子句并王城try
语句的执行. - 如果在执行
try
子句时发生了异常, 则跳过该子句的剩余部分, 然后, 如果异常的类型和except
关键子后面的异常匹配, 则执行except
子句, 然后据需执行try
语句之后的代码. - 如果发生的异常和
except
子句中指定的异常不匹配, 则将其传递到外部的try
语句中, 如果没有找到处理程序, 则它就是一个未处理的异常, 执行将停止并显示提示信息.
- 首先, 执行
- 一个 try 语句可能有多个 except 子句,以指定不同异常的处理程序. 最多之赐你个一个处理程序, 且只处理相应的try子句中发生的异常.
-
try ... except
语句有一个可选的else
子句, 在使用时必须放在所有的except
子句后面.- 对于在try 子句不引发异常时必须执行的代码来说很有用.
-
raise
语句允许程序员强制发生指定的异常.-
raise
唯一的参数就是要抛出的异常, 必须是一个异常实例或者是一个异常类. - 如果传递的是一个异常类,它将通过调用没有参数的构造函数来隐式实例化.
- 如果你需要确定是否引发了异常但不打算处理它,则可以使用更简单的 raise 语句形式重新引发异常.
-
- 用户自定义异常:
- 程序可以通过创建新的异常类来命名他们自己的异常, 异常应该直接或者间接地从
Exception
类派生. - 在创建可能引发多个不同错误的模块时,通常的做法是为该模块定义的异常创建基类,并为不同错误条件创建特定异常类的子类.
- 大多数异常都定义为名称以"Error"结尾.
- 程序可以通过创建新的异常类来命名他们自己的异常, 异常应该直接或者间接地从
-
try
语句有另一个可选子句, 用于定义必须在所有情况下执行的清理操作. -
finally
子句总会在离开try
语句前被执行, 无论是否发生了异常.- 它将在
finally
子句执行后被重新抛出. -
finally
子句对于释放外部资源(例如文件或者网络连接)非常有用, 无论是否成功使用资源.
- 它将在
- 预定义的清理操作:
- 某些对象定义了在不再需要该对象时要执行的标准清理操作, 无论使用该对象的操作是成功还是失败.
-
with
语句允许像文件这样的对象能够以一种确保它们得到及时和正确的清理的方式使用. - 执行完语句后,即使在处理行时遇到问题,文件也始终会被关闭.
类
- 类提供了一种组合数据和功能的方法.
- 创建一个新类意味着创建一个新类型的对象, 从而允许创建一个该类型的新实例.
- 每个类的实例可以拥有保存自己状态的属性.
- 一个类的实例也可以有改变自己状态的方法(定义在类中的).
- Python的类提供了面向对象编程的所有标准特性:类继承机制允许多个基类, 派生类可以覆盖它基类的任何方法, 一个方法可以调用基类中相同名称的的方法.
- 类也拥有Python天然的动态特性:它们在运行时创建, 可以在创建后修改.
- 类成员是public的.
- 所有成员函数都是virtual的.
- 方法函数使用表示对象的显式第一个参数声明,该参数由调用隐式提供.
- 类本身也是对象.
- 内置类型可以用作用户扩展的基类.
- 大多数具有特殊语法(算术运算符,下标等)的内置运算符都可以重新定义为类实例.
- 因为别名在某些方面表现得像指针.
Python作用域和命名空间
- namespace 是一个从名字到对象的映射. 大部分命名空间当前都由 Python 字典实现.
- 存放内置函数的集合(包含 abs() 这样的函数,和内建的异常等);
- 模块中的全局名称;
- 函数调用中的本地名称.
- 对象的的属性集合也是一种命名空间的形式. 不同命名空间中的名称之间绝对没有关系.
- 在表达式
modname.funcname
中,modname
是一个模块对象而funcname
是它的一个属性. - 在模块的属性和模块中定义的全局名称之间正好存在一个直观的映射:它们共享相同的命名空间!
- 包含内置名称的命名空间是在 Python 解释器启动时创建的,永远不会被删除.
- 模块的全局命名空间在模块定义被读入时创建.
- 模块命名空间也会持续到解释器退出.
- 每次递归调用都会有它自己的本地命名空间.
- 一个作用域是一个命名空间可直接访问的Python程序的文本区域.
- 使用 nonlocal 语句声明为非本地变量.
- 当前局部作为域将(按字面文本)引用当前函数的局部名称。 在函数以外, 局部作用域将引用与全局作用域相一致的命名空间:模块的命名空间。 类定义将在局部命名空间内再放置另一个命名空间.
- 在一个模块内定义的函数的全局作用域就是该模块的命名空间,无论该函数从什么地方或以什么别名被调用.
- 实际的名称搜索是在运行时动态完成的. 编译时 是朝着静态名称解析的方向演化的,因此不要过于依赖动态名称解析.
- 如果不存在生效的
global
语句, 对名称的赋值总是进入最内层作用域.- 赋值不会复制数据. 它们只是将名称绑定到对象.
- 语句 del x 会从局部命名空间的引用中移除对 x 的绑定.
- 所有引入新名称的操作都使用局部作用域.
-
import
语句和函数定义会在局部作用域中绑定模块或函数名称.
-
global
语句可被用来表明特定变量生存于全局作用域并且应当在其中被重新绑定. -
nonlocal
语句表明特定变量生存于外层作用域中并且应当在其中被重新绑定. - 局部赋值(这是默认状态)不会改变scope_test对spam的绑定. nonlocal赋值会改scope_test对spam的绑定, 而global赋值会改变模块层级的绑定.
初探类
- 类引入了一些新语法, 三种新对象类型和一些新语义.
class ClassName:
<statement-1>
<statement-N>
- 在类内部的函数定义通常具有一种特别形式的参数列表,这是方法调用的约定规范所指明的.
- 当进入类定义时,将创建一个新的命名空间,并将其用作局部作用域.
- 正常离开类定义时,将创建一个 类对象.
- 这基本上是一个包围在类定义所创建命名空间内容周围的包装器.
- 原始的(在进入类定义之前起作用的)局部作用域将重新生效,类对象将在这里被绑定到类定义头所给出的类名称.
- 类对象支持两种操作: 属性引用和实例化.
- 属性引用使用Python中所有属性引用所使用的标准语法:
obj.name
. - 有效的属性名称是类对象被创建时存在与类命名空间中的所有名称.
- 类属性也可以被赋值.
-
__doc__
也是一个有效的属性, 将返回类的文档字符串.
- 属性引用使用Python中所有属性引用所使用的标准语法:
- 类的实例化是使用函数表示法.
x=MyClass()
--- 创建新实例并将此对象分配给局部变量x.- 实例化操作会创建一个空对象, 许多类喜欢创建带有特定初始化状态的自定义实例.
- 类定义可能包含一个名为
__init__()
的特殊方法. - 当一个类定义了
__init__()
方法时, 类的实例化操作会自动为新创建的类实例发起调用__init__()
. -
__init__()
还可以有额外参数以实现更高灵活性, 提供给类实例化运算符的参数将被传递给__init__()
.
- 属性引用: 有两种有效的属性名称, 数据属性和方法.
- 数据属性不需要声明;像局部变量一样,它们将在第一次被赋值时产生.
- 方法是“从属于”对象的函数. 实例对象的有效方法名称依赖于其所属的类.
- 一个类中所有是函数对象的属性都是定义了其实例的相应方法.
- 方法的特殊之处就在于实例对象会作为函数的第一个参数被传入.
- 调用一个具有 n 个参数的方法就相当于调用再多一个参数的对应函数,这个参数值为方法所属实例对象,位置在其他参数之前.
- 实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法.
补充说明
- 数据属性会覆盖掉具有相同名称的方法属性.
- 方法名称使用大写字母, 属性名称加上独特的短字符串前缀(或许只加一个下划线), 或者是用动词来命名方法, 而用名词来命名数据属性.
- 类不能用于实现纯抽象数据类型. 在 Python 中没有任何东西能强制隐藏数据.
- 使用命名约定可以省去许多令人头痛的麻烦.
- 方法的第一个参数常常被命名为
self
.-
self
这一名称在Python中绝对没有特殊含义. - 任何一个作为类属性的函数都为该类的实例定义了一个相应方法.
- 将一个函数对象赋值给一个局部变量也是可以的.
- 方法可以通过使用
self
参数的方法属性调用其他方法. - 方法可以通过与普通函数相同的方式引用全局名称.
- 与方法相关联的全局作用域就是包含其定义的模块(类永远不会被作为全局作用域).
-
- 每个值都是一个对象, 因次具有类(类型), 并存储为
object.__class__
.
继承
- 派生类的定义语法为:
class DerivedClassName(BaseClassName):
<statement-1>
.
.
.
<statement-N>
- 名称 BaseClassName 必须定义于包含派生类定义的作用域中.
- 当基类定义在另一个模块中的时候,
class DerivedClassName(modname.BaseClassName)
. - 当构造派生类对象时, 基类会被记住. 用来解析属性引用.
- 如果请求的属性在派生类中无法找到, 就会转往在基类中查找.
- 搜索相应的类属性, 如有必要将按基类继承链逐步向下查找, 如果产生了一个函数对象则方法引用就生效.
- 派生类可能会重载其基类的方法, 调用同一基类中定义的另一方法的基类方法最终可能会调用覆盖它的派生类的方法.
- Python中所有的方法实际上都是
virtual
虚方法.
- Python中所有的方法实际上都是
- 在派生类中的重载方法实际上可能想要扩展而非简单地替换同名的基类方法,
BaseClassName.methodname(self, arguments)
. - Python有两个内置函数可被用于继承机制:
- 使用
isinstance()
来检查一个实例的类型.-
isinstance(obj, int)
仅会在obj.__class__为int或某个派生自int的类时为True.
-
- 使用
issubclass()
来检查类的继承关系.-
issubclass(bool, int)
为True, 因为bool是int的子类. -
issubclass(float, int)
为False, 因为float不是int的子类.
-
- 使用
多重继承
- 多重继承的语法为:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
- 从父类所继承属性的操作是深度优先、从左至右的,当层次结构中存在重叠时不会在同一个类中搜索两次.
- 如果某一属性在 DerivedClassName 中未找到,则会到 Base1 中搜索它,然后(递归地)到 Base1 的基类中搜索,如果在那里未找到,再到 Base2 中搜索,依此类推.
- 方法解析顺序会动态改变以支持对
super()
的协同调用. - 动态改变顺序是有必要的, 因为所有多重继承的情况都会显示出一个或更多的菱形关联.
- 动态算法会用一种特殊方式将搜索顺序线性化, 保留每个类所指定的从左至右的顺序,只调用每个父类一次,并且保持单调.
私有变量
- 仅限从一个对象内部访问的私有实例变量在Python中并不存在.
- 带有一个下划线的名称(例如 _spam)应该被当作是 API 的非仅供部分 (无论它是函数、方法或是数据成员).
- 定义私有成员可避免名称与子类所定义的名称相冲突. --- 名称改写.
- 任何
__spam
的标识符的文本将被替换为_classname_spam
, 其中classname
为去除了前缀下划线的当前类名称. - 名称改写有助于让子类重载方法而不破坏类内方法调用.
- 改写规则的设计主要是为了避免意外冲突.
- 访问或修改被视为私有的变量仍然是可能的.
- 任何
- Python中有没有真正的私有变量.
注意传递给 exec() 或 eval() 的代码不会将发起调用类的类名视作当前类.
- C语言中的
struct
结构体在Python中适合定义一个空类.- 一段需要特定抽象数据类型的 Python 代码往往可以被传入一个模拟了该数据类型的方法的类作为替代.
- 实例方法对象也具有属性: m.__self__ 就是带有m()方法的实例对象,而 m.__func__ 则是该方法所对应的函数对象.
迭代器
- 迭代器的使用非常普遍并使得 Python 成为一个统一的整体.
for line in open("myfile.txt"):
等.-
for
语句会调用容器对象中的iter()
. -
iter()
函数将返回一个定义了__next__()
方法的迭代器对象, 该方法将逐一访问容器中的元素. - 当元素用尽时,
__next__()
将引发StopIteration
异常来通知终止for循环. - 可以使用
next()
函数来调用__next__()
方法. - 定义一个
__iter__()
方法来返回一个带有__next__()
方法的对象, 如果类已定义了__next__()
, 则__iter__()
可以简单地返回self
.
class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, data): # 类变量对于所有的实例对象是共享的 self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration # 主动抛出异常 self.index = self.index - 1 return self.data[self.index]
-
生成器
-
Generator
是一个用于创建迭代器的简答而强大的工具.- 写法类似于标准的函数. 但要返回数据时会使用yield语句.
- 每次对生成器调用
next()
时, 它从上次离开位置恢复执行(会记住上次执行语句时的所有数据值).
def reverse(data): for index in range(len(data)-1, -1, -1): yield data[index]
- 可以用生成器来完成的操作同样可以使用迭代器来完成.
- 但生成器的写法更为紧凑, 因为生成器会自动创建
__iter__()
和__next__()
方法.
- 但生成器的写法更为紧凑, 因为生成器会自动创建
- 生成器另一个关键特性在于局部变量和执行状态会在每次调用之间自动保存.
- 除了会自动创建方法和保存程序状态,当生成器终结时,它们还会自动引发 StopIteration.
生成器表达式
- 某些简单的生成器可以写成简洁的表达式代码,所用语法类似列表推导式,将外层为圆括号而非方括号.
- 用于生成器将立即被外层函数所使用的情况.
- 相比等效的列表推导式则更为节省内存.
-
sum(i*i for i in range(10)) # sum of squares 先创建生成器再进行迭代
, 与列表推导式很类似.
标准库简介
- 操作系统接口:
-
os
模块提供了许多与操作系统交互的函数.- 一定要使用
import os
而不能使用from os import *
, 这可避免内置的open()
函数被os.open()
隐式替换掉. -
os.getcwd() # Return the current working directory, 返回当前目录
. -
os.chdir('dir') # Change current working directory, 改变当前目录
. -
os.system('mkdir today') # Run the command mkdir in the system shell, 在系统shell环境中执行mkdir today的命令
.
- 一定要使用
- 使用内置的
dir()
和help()
函数可用作交互式辅助工具, 用于处理大型模块. - 对于日常文件和目录管理任务,
shutil
模块提供了更易于使用的更高级别的接口.-
shutil.copyfile('data.db', 'archive.db')
. -
shutil.move('/build/executables', 'installdir')
.
-
-
文件通配符
-
glob
模块提供了一个在目录中使用通配符搜索创建文件列表的函数.
>>> import glob
>>> glob.glob('*.py')
['primes.py', 'random.py', 'quote.py']
- 命令行参数:
- 命令行参数存储在
sys
模块的argv
属性中, 运行python demo.py one two three
后, 执行import sys
和print(sys.argv)
将得到['demo.py', 'one', 'two', 'three']
. -
getopt
模块使用Unix getopt()函数的约定来处理sys.argv
. -
argparse
模块提供了更强大, 更灵活的命令行参数处理.
- 命令行参数存储在
- 错误输出重定向和程序终止:
-
sys
模块还具有stderr
,stdin
和stdout
的属性. - 终止脚本的最直接方式是使用
sys.exit()
.
-
字符串模式匹配
-
re
模块为高级字符串处理提供了正则表达工具.- 对于复杂的匹配和操作, 正则表达式提供简洁, 优化的解决方案:
>>> import re #导入正则表达式的模块 >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest') # 找到所有以b或者f开头的单词 ['foot', 'fell', 'fastest'] >>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat') # 去掉重复的the 'cat in the hat' >>> 'tea for too'.replace('too', 'two') # 用two替换掉too字符 'tea for two'
数学
-
math
模块提供对浮点数的底层C库函数的访问:-
math.cos(math.pi / 4)
. -
math.log(1024, 2)
.
-
-
random
模块提供了进行随机选择的工具:-
random.choice(['apple', 'pear', 'banana'])
. -
random.random() # random float
. -
random.randrange(6) # random integer chosen from range(6)
.
-
-
statistics
模块计算数值数据的基本统计属性(均值,中位数,方差等).-
statistics.mean(data)
. -
statistics.median(data)
.
-
互联网访问
- 许多模块可用于访问互联网和处理互联网协议.
-
urllib.request
用于从URL检索数据.-
from urllib.request import urlopen
.
-
-
smtplib
模块用于发送邮件.
-
- 日期和时间:
-
datetime
模块提供了以简单和复杂的方式操作日期和时间的类, 实现的终点是有效的成员提取以进行输出格式化和操作. -
from datetime import date
. -
now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
.
-
- 数据压缩:
- 常见的数据存档和压缩格式由模块直接支持, 包括: zlib, gzip, bz2, lzma, zipfile和tarfile.
-
zlib.crc32(s)
. -
t = zlib.compress(s)
. -
zlib.decompress(t)
.
- 性能测量:
- Python提供了一种可以立即回答这些问题的测量工具.
- 元组封包和拆包功能相比传统的交换参数可能更具吸引力.
-
timeit
模块可以快速演示在运行效率方面一定的优势. -
profile
模块和pstats
模块提供了用于在较大的代码块中识别时间关键部分的工具.
- 质量控制:
-
doctest
模块提供了一个工具, 用于扫描模块并验证程序文档字符串中嵌入的测试. - 测试构造就像将典型调用及其结果剪切并粘贴到文档字符串一样简单.
- `import doctest'
doctest.testmod() # automatically validate the embedded tests
-
unittest
模块允许在一个单独的文件中维护更全面的测试集.-
unittest.main() # Calling from the command line invokes all tests
.
-
-
- 包含电池:
-
xmlrpc.client
和xmlrpc.server
模块使远程过程调用实现了几乎无关紧要的任务.- 不需要直接了解和处理XML.
-
email
包是一个用于管理电子邮件的库.- 电子邮件包具有完整的工具集,用于构建或解码复杂的消息结构.
- 能实现互联网编码和标头协议.
-
json
包为解析这种流行的数据交换格式提供了强大的支持. -
csv
模块支持以逗号分隔值格式直接读取和写入文件,这些格式通常由数据库和电子表格支持. - XML处理由
xml.etree.ElementTree
,xml.dom
和xml.sax
包支持, 简化了应用程序和工具间的数据交换. -
sqlite3
模块是SQLite数据库库的包装器,提供了一个可以使用稍微非标准的SQL语法更新和访问的持久数据库. -
gettext
包,locale
包,codecs
包.
-
- 格式化输出:
-
reprlib
模块提供了一个定制化版本的repr()
函数, 用于缩略显示大型或深层嵌套的容器对象. -
pprint
模块提供了更加复杂的打印控制, 其输出的内置对象和用户自定义对象能够被解释器直接读取.- 美化输出机制会添加换行符和缩进, 以更清楚地展示数据结构.
-
textwrap
模块能够格式化文本段落, 以适应给定的屏幕宽度. -
locale
模块处理与特定地域文化相关的数据格式.-
locale
模块的format函数包含一个grouping
属性, 可直接将数字格式化为带有组分隔符的样式.
-
-
- 模板:
-
string
模块包含一个通用的Template
类, 具有适用于最终用户的简化语法, 允许用户在不再更改应用逻辑的情况下定制自己的应用.- 格式化操作是通过占位符实现的,占位符由 $ 加上合法的 Python 标识符(只能包含字母、数字和下划线)构成.
- 一旦使用花括号将占位符括起来,就可以在后面直接跟上更多的字母和数字而无需空格分割.
-
$$
将被转义成单个字符$
.
- Template 的子类可以自定义定界符.
- 模板的另一个应用是将程序逻辑与多样的格式化输出细节分离开来.
- 这使得对 XML 文件、纯文本报表和 HTML 网络报表使用自定义模板成为可能.
-
- 使用二进制数据记录格式:
-
struct
模块提供了pack()
和unpack()
函数, 用于处理不定长度的二进制记录格式. -
zipfile
模块, 循环遍历一个ZIP文件的所有头部信息.- '<'代表它们是标准尺寸的小尾型字节序.
-
多线程
- 线程是一种对于非顺序依赖的多个任务进行解耦的技术.
- 多线程可以提高应用的响应效率,当接收用户输入的同时,保持其他任务在后台运行.
- 将 I/O 和计算运行在两个并行的线程中.
-
threading
模块支持多线程的功能. 且不影响主程序的继续运行.
import threading, zipfile
class AsyncZip(threading.Thread):
def __init__(self, infile, outfile):
threading.Thread.__init__(self)
self.infile = infile
self.outfile = outfile
def run(self):
f = zipfile.ZipFile(self.outfile, 'w', zipfile.ZIP_DEFLATED)
f.write(self.infile)
f.close()
print('Finished background zip of:', self.infile)
background = AsyncZip('mydata.txt', 'myarchive.zip')
background.start()
print('The main program continues to run in foreground.')
background.join() # Wait for the background task to finish
print('Main program waited until background was done.')
- 多线程应用面临的主要挑战是,相互协调的多个线程之间需要共享数据或其他资源.
- threading 模块提供了多个同步操作原语,包括线程锁、事件、条件变量和信号量.
- 实现多任务协作的首选方法是将对资源的所有请求集中到一个线程中,然后使用 queue 模块向该线程供应来自其他线程的请求.
应用程序使用 Queue 对象进行线程间通信和协调,更易于设计,更易读,更可靠.
- 日志:
-
logging
模块提供功能齐全且灵活的日志记录系统.- 日志消息被发送到文件或sys.stderr.
- informational 和 debugging 消息被压制,输出会发送到标准错误流.
- 日志等级: DEBUG,INFO,WARNING,ERROR,和 CRITICAL.
-
- Python 会自动进行内存管理(对大多数对象进行引用计数并使用 garbage collection 来清除循环引用).
- 当某个对象的最后一个引用被移除后不久就会释放其所占用的内存.
-
weakref
模块提供的工具可以不必创建引用就能跟踪对象.- 当对象不再需要时,它将自动从一个弱引用表中被移除,并为弱引用对象触发一个回调.
- 对创建开销较大的对象进行缓存.
- 用于操作列表的工具:
-
array
模块提供了一种 array() 对象,它类似于列表,但只能存储类型一致的数据且存储密集更高. -
collections
模块提供了一种 deque() 对象,它类似于列表,但从左端添加和弹出的速度较快,而在中间查找的速度较慢. -
bisect
模块具有用于操作排序列表的函数. -
heapq
模块提供了基于常规列表来实现堆的函数.
-
- 十进制浮点运算:
-
decimal
模块提供了一种Decimal
数据类型用于十进制浮点运算.- 财务应用和其他需要精确十进制表示的用途.
- 控制精度.
- 控制四舍五入以满足法律或监管要求.
- 跟踪有效小数位.
- 用户期望结果与手工完成的计算相匹配的应用程序.
- Decimal 表示的结果会保留尾部的零,并根据具有两个有效位的被乘数自动推出四个有效位。 Decimal 可以模拟手工运算来避免当二进制浮点数无法精确表示十进制数时会导致的问题.
-
decimal
模块提供了运算所需要的足够精度.
-
虚拟环境和包
- 创建一个virtual environment,一个目录树,其中安装有特定Python版本,以及许多其他包.
- 不同的应用可以使用不同的虚拟环要解决先前的冲突需求示例,应用程序A可以拥有自己的1.0版本安装虚拟环境,而应用程序B则具有2.0版本的另一个虚拟环境.
- 用于创建和管理虚拟环境的模块称为 venv.
-
venv
通常会安装你可用的最新版本的 Python.
-
- 创建虚拟环境:
- 将
venv
模块作为脚本运行目录路径,python3 -m venv tutorial-env
. - 如果它不存在,这将创建 tutorial-env 目录,并在其中创建包含Python解释器,标准库和各种支持文件的副本的目录.
- 激活虚拟环境:
source tutorial-env/bin/activate
.
- 将
- 使用pip管理包
- pip 的程序来安装、升级和移除软件包.
- pip 有许多子命令:“search”、“install”、“uninstall”、“freeze”等等.
- 通过提供包名称后跟 == 和版本号来安装特定版本的包.
- 运行 pip install --upgrade 将软件包升级到最新版本.
- pip uninstall 后跟一个或多个包名称将从虚拟环境中删除包.
- pip show 将显示有关特定包的信息.
- pip list 将显示虚拟环境中安装的所有软件包.
- pip freeze
将生成一个类似的已安装包列表,但输出使用 pip install 期望的格式. *
pip freeze > requirements.txt`.
- 可执行的Python脚本:
- 第一行添加
#!/usr/bin/env python3.7
.- #! 必须是文件的前两个字符.
- 散列或磅字符'#'在Python中代表注释开始.
- 第一行添加