教程
2025年9月16日10:43:07
python3.13 的官方教程
本教程是一个整体教程, 并非包含所有细节. 并且不是编程的入门教程, 只是 python 的入门教程.
1. 课前甜点
Python 是解释性程序, 对于短小的任务, 可以更为快速的处理.
如果不厌烦类型, 编译, 甚至是 IDE 等繁琐的操作, 其实用 C++ 等其他高级语言也一样. 个人习惯 JavaScript(Node), 也可以处理日常的小东西, 但是 Python 的生态实在是太强大了. 特别是 C 开发的库, Node 使用 C 开发的库, 有些需要本地 node-gyp 来构建, 易用上不如 Python. 主要是 Numpy 太强大.
另外 Python 的命名一开始并不来自蟒蛇. 它源自 BBC 的 "Monty Python 飞行马戏团".
2. 使用 Python 解释器
2.1. 换出解释器
首先介绍怎么找 python 的安装目录 (略).
运行 python 命令后进入交互式解释器. 退出使用 quit()
. 其他方式可以忽略.
一些特殊的启用解释器的方式:
python -c "命令"
执行字符串.python -m "模块"
可以执行模块.
2.1.1. 传入参数
要获得命令行参数, 使用 sys
模块下的 argv
列表.
# index.py
import sys
for v in sys.argv:
print(v)
需要注意的是:
python
解释器会忽略-c
,-m
之后的内容, 其内容会交给执行的内容去处理.python 文件.py
命令会将python
后的所有都放到sys.argv
中.python -c 命令
, 其中sys.argv[0]
就是-c
, 需要注意的是会忽略命令本书.python -m 模块名
, 其中sys.argv[0]
是模块的全路径.
python -c 'import sys; print(sys.argv)' 1 2 a b
# => ['-c', '1', '2', 'a', 'b']
python -m index 1 2 a b
# =>
# C:\Users\jk\Desktop\py-demo\index.py
# 1
# 2
# a
# b
python .\index.py 1 2 a b
# =>
# .\index.py
# 1
# 2
# a
# b
2.1.2. 交互模式
执行 python
后进入交互模式, 终端会显示 >>>
所谓提示符.
使用 quit()
退出.
2.2. 解释器的运行环境
2.2.1. 源文件字符编码
默认会使用 utf-8
编码, 如需要跟换, 在脚本第一行使用
# -*- coding: 编码 -*-
例如, 使用 Windows-1252 编码
# -*- coding: cp1252 -*-
若 Linux 上使用 shebang, 可以写成
#!/usr/bin/env python3
# -*- coding: cp1252 -*-
3. Python 速览
python 使用 #
作为行注释的开头.
3.1. Python 用作计算器
将 Python 解释器当做强大的计算器来使用.
好像很多解释性语言都支持, 比如 node.
3.1.1. 数字
- 解释器会自动判断数字的字面量, 将其作为
int
或float
来使用. 混合运算会自动转换为浮点数. 这一点与传统的 C 语言一样. - 数字运算使用: 加 (
+
), 减 (-
), 乘 (*
), 除 (/
, 浮点数除法), 整除 (//
), 乘幂 (**
). 优先级与普通认知一样. - 使用圆括号可以提升优先级.
- 使用等号来赋值
=
. 使用变量存储结果. 变量不用声明, 直接使用 (弱类型). 未赋值的变量无法使用. - 交互模式下具有记忆, 可以使用前一个步骤中的变量来进行计算 (有点 matlab 解释器的既视感).
建议交互模式下, 将变量当做只读的来用. 可以避免很多错误.
Python 支撑的类型很多, 例如: decimal, fraction, 甚至是复数.
3.1.2. 文本
文本, 即字符串. 使用单引号, 或双引号括起来.
- 与其他语言一样, 使用斜线得到转义字符串.
- 默认字符串是不允许跨行的, 使用三引号得到可以跨行的字符串. 跨行字符串中会在内行嵌入
\n
, 如果需要取消将\
插入在换行前. - 字符串可以使用乘法,
"a" * 3
得到"aaa"
. - 连接字符串使用加号 (
+
). - 两个相邻的字符串会自动合并. 例如:
"a" 'b' "c"
逻辑上是"abc"
. 在超长字符串分行书写时很有用 (不用再添加加号). 注意只能是字符串字面值. - 字符串使用下标索引来访问单个字符, python 是 base-0 的索引规则. 支持负索引,
-1
表示最后一个字符. 需要注意-0
与0
等价. - 字符串也支持切片. 索引语法:
str[start:end:step]
- 采用左闭右开的区间来获取子字符串.
step
是步长, 默认为1
, 即后一个取值的索引值与前一个取值的索引值的差.- 左右边界可省略,
start
默认为0
,end
默认为字符串长度 (取不到, 注意逻辑上不是-1
). - 字符串是不可变的 (与其他编程语言一样), 索引越界会报错.
- 获得字符串长度使用
len(str)
.
3.1.3. 列表
列表是复合数据类型, 可以存储多种类型的数据.
- 使用方括号界定.
- 使用逗号分隔.
注意列表可以存储不同的类型, 只是通常在使用时会尽可能使用同一种类型.
与字符串一样, 列表也支持切片.
列表使用加号, 可以将两个列表合并.
列表是可变的 (字符串不可变), 可以修改对应索引处的内容, 例如:
list[index] = value
.列表是对象, 使用
append(value)
方法可以在列表尾部追加元素.列表是引用类型. 使用切片可以实现浅拷贝. 例如:
newList = oldList[:]
.切片赋值, 可以修改列表结构. 例如
list[s:e] = []
相当于清空[s,e]
处的内容. JS 中也有类似的方法, 例如splice
, 区别在于splice
的第三个参数是展开的, 不是数组.使用
len(list)
获得列表长度.列表的元素可以是任意类型, 也可以是列表.
可以使用
in
运算符判断一个值是否在列表中.
3.2. 走向编程的第一步
文档给出了一个示例, 计算 Fibonacci 数列.
a, b = 0, 1
while a < 10:
print(a)
a, b = b, a + b
这段代码非常巧妙的适用了 Python 的语法特性:
- 首先介绍了
while
循环的结构, 注意冒号, 缩进. - 介绍了关系运算符. 关系运算符与其他语言类似:
==
,>
,<
,!=
,>=
,<=
. - 特殊的赋值运算.
print()
可以接收多个参数, 会将多个参数使用空格分隔进行输出.- 默认
print()
会在结尾处使用\n
换行, 可以替换掉:print(..., end = ',')
.
4. 更多流程控制
while
循环:
while boolean 表达式:
循环体
4.1. if 语句
语法:
if 表达式:
语句
elif 表达式:
语句
else:
语句
python 没有
switch
, 取而代之的是match
语句.
4.2. for 语句
不同于其他语言, for 仅仅是按照顺序将列表中的元素一个个取出来.
for 迭代变量 in 列表:
循环体
如果遍历的是键值对 (花括号分隔, 键值用冒号分隔, 键需要使用引号括起来, 键值对用逗号分隔, 格式如同 JSON), 那么迭代的是键.
4.3. range() 函数
用于生成序列, 参数与切片参数一样, 采用左闭右开区间来生成. 默认步长为 1
- 需要注意
range()
生成的是range
类型的数据. 但可以直接 for 遍历. - 使用
list()
可以将其转换为列表类型. - 参数支持负数.
可以借助 range()
生成索引序列, 通过遍历序列来遍历列表:
for i in range(len(list)):
操作 list[i]
range
不是真正的列表, 但是使用起来很像列表, 它有点 C# 中 Linq 延迟执行的味道. python 中也有类似于sun()
等这类直接迭代求值的方法.
4.4. break 和 continue 语句
与其他编程语言一样.
4.5. 循环的 else 语句
与 for
和 while
配对的 else:
语句. 逻辑上是, 如果循环正常结束, 就会执行 else:
中的代码. 这个不正常结束就是 break
, return
等.
4.6. pass 语句
放在空结构中, 逻辑上相当于占位. 由于 python 使用缩进来描述一个逻辑块, 当逻辑块是空的时候使用 pass
来充当. 常用与循环, 函数内等.
4.7. match 语句 (*)
逻辑上就是其他语言中的 switch 语句. 但是更为强大. 猜测 C# 中的 模式匹配就是参考它的.
语法:
match 表达式:
case 匹配项:
语句
case 匹配项:
语句
case _:
默认语句
注意, 默认 match 不会像 switch 那样穿透, 匹配到了只会执行那一项的代码.
匹配项很强大:
- 可以是简单字面量, 如数字, 字符串等.
- 可以是使用
|
连接的多个字面量, 描述为或者. - 可以是元组 (括号括起来, 使用逗号分隔). 在元组中只是使用变量, python 会对数据解包, 并赋值给变量.
- 可以使用类来组织匹配, 定义好类的构造函数与参数. 利用构造函数与参数来匹配.
- 在匹配后, 冒号前, 可以使用
if
作为守卫, 在匹配上后, 满足该判断才会进入. 可以结合解包变量赋值来运用.
这个 match 的适用很灵活, 应该注意.
细节可以参考: https://docs.python.org/zh-cn/3.13/tutorial/controlflow.html#match-statements
4.8. 定义函数
语法:
def 函数名(参数列表):
"""文档注释"""
函数体
- 函数内部使用局部变量符号表. 即默认都是局部变量.
- 函数名也可以赋值, 使用另一个名字来调用函数.
- 函数默认返回值是
None
, 要显式返回使用return
. - 函数与方法的定义语法是一致的.
4.9. 函数定义详解
函数定义支持可变参数, 这里有三种可以进行组合使用.
4.9.1. 默认值参数
与其他编程语言一样, 定义时使用赋值语句给定默认值, 调用时不传参可以使用默认值, 传参使用传入值. 默认值必须在参数末尾处.
语法与使用与其他语言没什么不同
默认值在 Python 中有点像全局静态变量的感觉. 特别是引用类型. 例如
def f(a, L = []):
L.append(a)
return L
print(f(1)) # => [1]
print(f(2)) # => [1,2]
print(f(3)) # => [1,2,3]
给人感觉就好像是一个全局静态字段引用了列表一样. 每次的操作实际上都是对这个列表进行处理. 要避免这个问题可以使用:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
4.9.2. 关键字参数
关键字参数如同其 C# 中的命名参数.
- 可以不参数定义顺序的限制, 可以直接使用
name=value
的形式传参. - 函数调用时, 具名参数与普通参数可以同时存在, 但具名参数必须在后面.
定义函数时, 还有两个特殊的参数形式:
*arguments
, 一个前置星号. 接收任意个简单参数, 然后装包成一个元组, 逻辑上与 C# 的params
参数一样. 注意传入一个元组是只会视为一个元素 (这一点与 C# 不同).**keywords
, 两个前置星号. 接收任意个键值对, 即key=value
形式的参数. 然后装包成键值对. 注意, 直接传入键值对会报错.
定义函数时, 这两个参数可以同时出现, 但是 键值对 必须放在 元组 的后面.
如果传入参数时就是一个元组或键值对, 需要对应上 *args
, **kv
参数, 那么就需要解包后传参. 例如:
def func(**kv):
for k in kv:
print(k + '=>' + str(kv[k]))
func(**{
"name": "jim",
"age": 19
})
4.9.3. 特殊参数
就是在定义函数的时候, 显式的给定两个标记, 表示怎么传递参数.
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
----------- ---------- ----------
| | |
| 位置或关键字 |
| - 仅限关键字
-- 仅限位置
/
前的参数必须使用位置传参, 不允许使用关键字传参/
与*
之前的参数, 既可以用位置, 也可以用关键字的方式传参*
之后的参数必须使用关键字方式传参, 不得使用位置方式传参
逻辑上, 下面两个函数定义是等价的.
def f1(a, b, c):
pass
def f2(/, a, b, c, *):
pass
只允许位置传参的函数
def f(a, b, c, /):
pass
只允许键值对传参的函数
def f(*, a, b, c):
pass
4.9.4. 任意实参列表
实际上就是 *args
参数. 需要说明的是, 该参数后只允许有关键字参数.
4.9.5. 解包实参列表
针对传参, 如果传入元组, 键值对不解包就当做一个参数对象来看待.
4.9.6. Lambda 表达式
与 C# 或 JS 一致, 仅仅语法形态不同:
lambda a,b: a + b
- 使用
lambda
关键字引导. - 参数在冒号前.
- 表达式在冒号后.
4.9.7. [略]文档字符串
基本略.
可以使用
函数名.__doc__
输出文档注释
4.9.8. 函数注解
可以理解为类型描述, 该函数的参数与返回值标注类型. 记住一个示例即可:
4.10. [略]小插曲: 编码风格
5. 数据结构
可以理解为复合数据类型的使用方法合集.
5.1. 列表详解
常用列表方法
方法 | 含义 |
---|---|
append(a, /) | 在列表末尾追加元素 |
extend(list, /) | 使用一个可迭代数据作为参数, 追加至列表尾部. |
insert(i, a, /) | 在索引处插入元素. |
remove(a, /) | 移除第一个出现的元素, 找不到触发 ValueError 异常. |
pop([i], /) | 返回 i 位置处的元素, 无参数时返回列表末尾处的元素. 然后再列表中移除该元素. |
clear() | 清空列表. |
index(x[, start[, end]]) | 返回元素 x 的索引, 后面的参数用于控制检索范围. |
count(x) | 统计元素的次数. |
sort(*, key=None, reverse=False) | 对原列表排序. |
reverse() | 翻转列表. |
copy() | 浅拷贝. |
列表是可变的. 通常对可变数据进行操作, 如
insert
,remove
,sort
等, 方法不返回数据, 即返回None
.
5.1.1. 用列表实现堆栈
对标 JavaScript 中的用法模型, Python 也一样. 使用方法: append()
, pop()
.
5.1.2. 用列表实现队列
考虑到队列在添加元素, 移除元素的过程中, 数据的索引会有所更新, 使用列表来实现队列效率很低.
应该使用 collections.deque
来实现队列, 涉及方法有: append()
和 popleft()
.
from collections import deque
queue = deque([...])
queue.append(...) # 入队
queue.popleft() # 出队
5.1.3. 列表推导式
重要模式.
基于列表元素生成新的列表, 不同于 JavaScript 中常用 map
方法, Python 多用列表推导式. 基本语法是:
[表达式 for 迭代变量 in 列表 if 过滤条件]
其中 if
过滤条件是可选的. 逻辑上相当于先 filter
, 再 map
.
迭代允许嵌套, 执行顺序是从左往右的 (这个是高级的). 例如将元素为列表的列表扁平化, 可以使用:
list = [[1,2],[3,4],[5,6]]
newlist = [item for sublist in list for item in sublist]
在 Python 中不推荐使用 for
循环来迭代生成数据, 因 for
/if
无作用域, 会引入迭代变量, 从而污染作用域.
Python 也有 map
函数, 第一个参数是 lambda 表达式, 第二个参数是列表. 例如:
list = list(map(lambda x => x**2, range(10)))
生成笛卡尔积的示例:
[(x, y) for x in (1,2,3) for y in (1,2,3)]
5.1.4. 嵌套的列表推导式
列表推导式中的表达式可以是另一个推导式.
补充一个函数 zip(*iterables, strict=False)
- 延迟执行, 该函数返回一个迭代器 (有点 Linq 的味道)
- 依次迭代参数中的每一个迭代器, 并将其迭代的元素取出, 合并成一个新的元组作为迭代数据返回.
- 默认并未严格要求参数中的每一个迭代器的长度, 默认遇到最短的迭代项结束, 参数
strict
可以指定是否会因为长度不同而报错.
如果迭代的是非等长的列表, 可以使用 itertools.zip_longest(*iterables, fillvalue=None)
来代替.
zip_longest('ABCD', 'xy', fillvalue='-') → Ax By C- D-
5.2. del
语句
del
可以基于切片语法在列表中删除片段, 以及删除整个变量.
语法: del 变量
或 del 列表[切片语法]
5.3. 元组和序列
内置的序列类型有: 列表, 元组, 以及 range
. 本节主要介绍元组.
元组不可变, 列表可边, 其他几乎一样. 但是元组的成员可以是可变的.
创建语法比较特殊:
- 空元组, 可以使用一个空的圆括号.
- 单个元素的元组比较特殊, 一般使用一个元素加一个逗号, 圆括号可以省略.
元组 (列表) 支持解构语法, 例如: a, b, c = <tupleExpression>
5.4. 集合
逻辑上与数学中的集合概念相同: 无序, 不重复数据的集合. 并且包含集合运算方法.
创建使用花括号, 或者使用 set()
函数. 需要注意的是, 空集合只能使用 set()
函数, 因为 {}
是字典.
集合运算包括:
- 元素的属于运算:
in
- 集合的交:
&
- 集合的并:
|
- 集合的差:
-
- 集合的对称差:
^
集合也支持推导式 (逻辑上可以看成特殊的列表).
5.5. 字典
映射类型, 与其他编程语言一致. 在 Python 中, 键是不可变类型, 如果元组仅包含数字或字符串 (基本值类型), 那么元组也可以作为键使用.
- 创建字典的语法与
JSON
语法一致. 注意键必须使用引号括起来. del
允许删除字典中的某个键值对.- 访问字典可以使用索引, 但是索引不存在时会异常, 可以使用
get()
方法, 键不存在时返回None
. list(字典)
会得到键构成的列表, 顺序是插入的顺序.- 判断字典中是否存在某键, 可以使用
in
运算符, 例如:key in dic
.
dict()
构造函数可以创建键值对. 也可以使用推导式来创建键值对.
dict([(k, v), (k, v), ...])
dict(k = v, k = v, ...)
[k: v for v in list]
5.6. 循环技巧
for
循环默认遍历列表, 元组, 字典, 以及集合时:
- 列表/元组迭代的是项
- 字典迭代的是键
- 集合迭代的是元素.
如果要迭代键与内容:
- 列表使用
enumerate()
可以迭代索引与项. - 字典使用
items()
方法可以迭代键与项. 字典遍历默认使用keys()
方法, 也可以使用values()
方法迭代值 - 多个列表, 可以使用
zip()
来并行迭代, 生成新的结构.
一般来说, 如果迭代中会对元素进行修改, 不要直接对原始数据修改, 创建一个副本进行处理.
5.7. 深入条件控制
主要介绍逻辑表达式. if
和 while
等结构中不仅仅只有比较, 还有 and
, or
, not
, in
等.
注意, Python 中链式关系运算符的判断, 与数学表达式一致. 即, 支持 3 > 2 > 1
形式的判断.
同时需要注意, 连续的 and
等运算与其他编程语言一样, 也存在逻辑中断的特性.
5.8. 序列与其他类型比较
同类型的序列 (列表, 元组, range
等) 可以进行大于, 小于, 等于的比较, 采用字典排序法的规则来进行比较.
6. 模块
保存代码的文件就被视为模块. 文件使用模块名和后缀名 (.py
) 构成.
导入模块后, 使用
__name__
可以获得模块名.
6.1. 模块详解
模块使用 import
来导入, 仅在在第一次遇到的时候执行.
导入模块的方式:
- 导入模块, 并用模块名作为命名空间:
import 模块名
. 将模块名混入当前作用域, 将模块名当做命名空间使用. - 导入模块内的某个成员:
from 模块名 import 成员名
. 注意不会在当前作用域添加模块名.
模块内有自己的作用域, 不会出现污染问题.
导入的内容允许使用 as
重新命名.
允许使用 *
来导入所有, 但不建议这么使用.
6.1.1. 以脚本的形式执行模块
实际上就是在模块内部添加下面代码:
if __name__ == "__main__":
# 以脚本形式运行才会执行该处代码
pass
6.1.2. 模块搜索路径
导入模块时, 优先搜索内置模块, 然后利用 sys.path
来进行搜索模块.
- 内置模块的路径由
sys.builtin_module_names
决定. - 模块的搜索路径由
sys.path
来决定 (脚本所在目录, 环境变量PYTHONPATH
, 以及依赖安装的默认值, 一般是site-packages
).
6.1.3. 已编译的 Python 文件
为了加载效率 (仅仅是加载模块), Python 会将模块编译后放在 __pycache__
目录下. 并使用模块名, 版本号来命名.
6.2. 标准模块
就是内置模块. 略
6.3. dir()
函数
该函数可以查找模块定义的名称.
- 直接使用
dir(模块名)
. - 无参数时列出的是当前定义的名称.
dir
不会列出内置名称.
6.4. 包 (raw)
包提供了一个模块集合, 相当于提供了子命名空间的逻辑. 通常是为了将一组紧密联系在一起的子模块组合在一起. 这类模块的访问允许在模块名中通过点来划分层级.
包的目录下必须含有一个 __ini__.py
文件, 来描述包内模块的组织. 有这个文件 Python 才会将该目录当做包来处理. 即使 __init__.py
是一个空文件. 实际上 __init__.py
里面是初始化的代码, 以及对 __all__
的赋值.
然后介绍了导入的几种语法, 以及导入后如何引用的语法.
文档地址: https://docs.python.org/zh-cn/3.13/tutorial/modules.html#packages
6.4.1. 从包中导入 *
此时就需要为 __all__
初始化了, 它表示 *
可以导入的名称. 该名称可以是子包名, 或成员名, 是一个字符串列表.
6.4.2. 相对导入
使用点表示当前目录, 两个点表示上级目录, 然后使用:
from . import xxx
from .. import xxx
6.4.3. 多目录中的包(略)
7. 输入与输出
主要介绍的是格式化字符串, 文件写入, 以及 JSON 格式的数据.
7.1. 更复杂的输出格式
- 格式化字符串, 在字符串前使用
F
或f
. - 使用字符串的
format()
方法. 关于格式参考文档. - 利用字符串切片与合并来构造格式化字符串.
如果不需要格式, 可以使用字符串转换函数: str()
与 repr()
. 第二个函数会包含代码细节的格式. 如引号, 转义字符串等.
文档中然后详细说明了上述内容的用法, 这里仅补充手动格式化的方法.
- 字符串方法:
rjust()
, 在字符串左边添加指定个数的空格来实现对齐. - 对应的还有:
ljest()
,center()
. 一个是右边添加空格, 一个是两端添加空格. - 字符串的
zfill()
方法, 在左边填充0
.
7.2 读写文件
使用 open
函数获得文件句柄.
f = open('fileName', 'mode', encoding='utf-8')