Python学习笔记

Python学习笔记

小叶子

封面作者:NOEYEBROW

请先阅读JavaScript学习笔记或同时学习相关基础课程, 本笔记参考Python官方文档完成

⭐基础

安装和运行

安装方法 描述
官网下载 https://www.python.org/downloads/
使用 Anaconda https://www.anaconda.com/products/individual
使用 homebrew brew install python[@xxx]
代码编辑器 描述
VSCode https://code.visualstudio.com/
PyCharm https://www.jetbrains.com/pycharm/download/
北师大有购买专业版, 可在正版软件平台下载
1
2
hello_str = 'Hello, world!' # 变量使用 snake_case 命名
print(hello_str)
运行命令 描述
python --version 查看 Python 版本, 确认安装成功
python 进入 Python 解释器, 运行 quit() 退出
python xxx.py 运行 Python 脚本
python xxx.py arg1 arg2 运行 Python 脚本并传入参数, 可以使用 sys.argv 获取参数

注释

1
2
3
4
5
6
7
8
# 单行注释

'''
多行注释
多行注释
多行注释
'''
# 注: 多行注释实质是字符串, 只是没有赋值给变量

文档注释

文档注释是函数的第一个语句, 用于描述函数的功能和参数. 文档注释可以被代码编辑器识别, 并用于自动补全和提示

1
2
3
4
5
6
7
8
9
10
def add(arg1, arg2):
'''
计算两个数的和
'''
return arg1 + arg2


# 查看文档注释
help(add)
print(add.__doc__)

如果代码编辑器支持, 也可以使用 Markdown 语法书写文档注释

数据类型

类型 描述 示例
int 整数 18
float 浮点数 1.5
complex 复数 1 + 2j
str 字符串 'xiaoyezi'"xiaoyezi"'''多行字符串'''
bool 布尔值 TrueFalse
NoneType 空值 None
list 列表 [1, 2, 3]
tuple 元组 (1, 2, 3)
dict 字典 {'name': 'xiaoyezi', 'age': 18}
set 集合, 元素不可重复 {1, 2, 3}

字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 通过索引访问字符串中的字符
name = 'xiaoyezi'
print(name[0]) # x
print(name[-1]) # i, 即倒数第一个
print(name[1:3]) # ia, 从 str[1] 到 str[3-1]
print(name[1:]) # iaoyezi, 从 str[1] 到末尾
print(name[:3]) # xia, 从头到 str[3-1]
# 注: 切片可以自动处理索引越界

# 字符串不可变, 不能直接修改
name[0] = 'X' # 报错

# 转义字符
print('I\'m a student') # I'm a student

# 字符串拼接和重复
print('Hello, ' + 'world!') # Hello, world!
print('Hello, ' * 3) # Hello, Hello, Hello,

# 多行字符串取消行尾换行符
intro = '''\
I'm a student
I'm 18 years old
'''

# 相邻字符串字面量会自动连接
print('Hello, ' 'world!') # Hello, world!
print(
'Hello, '
'world!'
) # Hello, world!

# 字符串长度
print(len('xiaoyezi')) # 8

# 字符串方法
print('xiaoyezi'.capitalize()) # Xiaoyezi
print('xiaoyezi'.upper()) # XIAOYEZI
print('xiaoyezi'.lower()) # xiaoyezi
print('xiaoyezi'.title()) # Xiaoyezi
print('xiaoyezi'.center(20, '*')) # ******xiaoyezi******
print('xiaoyezi'.ljust(20, '*')) # xiaoyezi************
print('xiaoyezi'.rjust(20, '*')) # ************xiaoyezi
print('xiaoyezi'.count('i')) # 2
print('xiaoyezi'.find('i')) # 1, 未找到返回 -1
print('xiaoyezi'.rfind('i')) # 7, 未找到返回 -1
print('xiaoyezi'.replace('i', 'I')) # xIaoyezi
print('xiaoyezi'.split('o')) # ['xia', 'yezi']
print('xiaoyezi'.strip('i')) # aoyez, 删除开头和结尾的字符
print('xiaoyezi'.lstrip('i')) # aoyezi, 删除开头的字符
print('xiaoyezi'.rstrip('i')) # xiaoyez, 删除结尾的字符
print('xiaoyezi'.startswith('x')) # True
print('xiaoyezi'.endswith('i')) # True
print('xiaoyezi'.isalpha()) # True, 是否全是字母
print('xiaoyezi'.isdigit()) # False, 是否全是数字

格式化字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
name = 'xiaoyezi'
age = 18
print('My name is %s, I am %d years old' % (name, age))
print('My name is {}, I am {} years old'.format(name, age))
print(f'My name is {name}, I am {age} years old')

# 表达式文本
print(f'{name=} {age=}') # name='xiaoyezi' age=18
# 相当于
print(f'name=\'{name}\' age=\'{age}\'')

# 填充字符串
print(f'I am {age:10}') # 'I am 18' 最小宽度为 10, 默认填充空格
print(f'I am {age:*^10}') # 'I am ****18****', 使用 * 填充, 居中对齐
print(f'I am {age:0>10}') # 'I am 0000000018', 使用 0 填充, 右对齐
print(f'I am {age:0<10}') # 'I am 1800000000', 使用 0 填充, 左对齐

# 按类型输出字符
idea = '哈哈哈'
print(f'I am {idea!a}') # ASCII 字符, '\u54c8\u54c8\u54c8'
print(f'I am {idea!s}') # 字符串 (str()), 哈哈哈
print(f'I am {idea!r}') # 字符串 (repr()), '哈哈哈'

# 按类型输出数字
print(f'I am {age:b}') # 二进制 '10010'
print(f'I am {age:#b}') # 二进制 '0b10010'
print(f'I am {age:o}') # 八进制 '22'
print(f'I am {age:#o}') # 八进制 '0o22'
print(f'I am {age:x}') # 十六进制 '12'
print(f'I am {age:X}') # 十六进制 (大写) '12'
print(f'I am {age:#x}') # 十六进制 '0x12'
print(f'I am {age:e}') # 科学计数法 '1.800000e+01'
print(f'I am {age:.2f}') # 保留两位小数 '18.00'
print(f'I am {age:.0f}') # 保留整数 '18'

# 显示正负号
print(f'I am {age:+}') # '+18'
print(f'I am {-age:+}') # '-18'

列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 创建列表
lis = [1, 2, 3]
print(lis[0]) # 1
print(lis[-1]) # 3
print(lis[1:3]) # [2, 3]
print(lis[1:]) # [2, 3]
print(lis[:2]) # [1, 2]
print(len(lis)) # 3

# 列表可变
lis[0] = 0
print(lis) # [0, 2, 3]

# 拼接
lis += [4, 5] # 即 lis = lis + [4, 5]

# 删除, 使用 del 关键字
list = [1, 2, 3, 4, 5]
del list[0] # [2, 3, 4, 5]
del list[1:3] # [2, 5]
del list[:] # []
# 还可以 del list, 此时再访问 list 变量会报错

# 和 JavaScript 类似, Python 的列表变量存储的是对象的引用, 而不是对象本身
# 如果将一个列表赋值给另一个变量, 两个变量指向同一个列表, 修改其中一个会影响另一个
lis1 = [1, 2, 3]
lis2 = lis1
lis2[0] = 0
print(lis1) # [0, 2, 3]

# 复制列表
lis1 = [1, 2, 3]
lis2 = lis1.copy() # 浅拷贝
lis3 = lis1[:] # 浅拷贝

# 深拷贝
import copy
lis1 = [[1, 2], [3, 4]]
lis2 = copy.deepcopy(lis1)

# 列表解构赋值
a, b = [1, 2]
# 交换两个变量的值
a, b = b, a
方法 描述
lis.append(x) 在列表末尾添加一个元素
lis.extend(iterable) 一次性追加多个值
lis.insert(i, x) 在位置 i 插入元素 x
lis.remove(x) 删除第一个值为 x 的元素
lis.pop([i]) 删除指定位置的元素, 默认删除最后一个元素
lis.index(x) 返回第一个值为 x 的元素的索引
lis.count(x) 返回 x 在列表中出现的次数
lis.sort() 排序, 默认升序
lis.reverse() 倒序
lis.clear() 清空列表

字典

  • 字典是无序的, 所以不能用索引来获取值
  • key 不能重复, 可以是字符串、整数、浮点数、元组(不可变的变量)
  • 要添加一个 key-value 对, 可以直接 dic[key] = value, 也可以使用 update 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dic = {'name': 'xiaoyezi', 'age': 18}
print(dic['name']) # xiaoyezi
print(dic.name) # 报错, 不能使用点号获取值

# 删除, 使用 del 关键字
del dic['name']
del dic['height'] # 不存在的 key 会报错
# 还可以 del dic, 此时再访问 dic 变量会报错

# 判断 key 是否存在
print('name' in dic) # False
print('name' not in dic) # True

# 设置值
dic['height'] = 180
方法 描述
dic.keys() 返回字典中所有 key 的可迭代对象
dic.values() 返回字典中所有 value 的可迭代对象
dic.items() 返回字典中所有 key-value 对的可迭代对象
dic.get(key[, default]) 获取 key 对应的值, 如果 key 不存在则返回 default
dic.setdefault(key[, default]) 如果 key 不存在则设置 keydefault
dic.pop(key[, default]) 删除 key 对应的 key-value 对, 如果 key 不存在则返回 default
dic.popitem() 随机删除一个 key-value 对, 返回删除的 key-value
dic.clear() 清空字典
dic.copy() 复制一个字典(浅拷贝)
dic.update({key: value}) 更新字典中的 key-value
dic.update(key=value) 更新字典中的 key-value

集合

  • 集合是无序的, 元素不可重复
  • 集合是可变的, 可以添加或删除元素
操作 描述
set() 创建一个空集合, 不能使用 {} 创建空集合, {} 创建的是空字典
set(iterable) 从可迭代对象创建一个集合
set.add(x) 添加元素到集合, 如果元素已存在, 则不进行任何操作
set.remove(x) 移除集合中的元素, 如果元素不存在, 则会发生错误
setA & setB 取两个集合的交集
setA | setB 取两个集合的并集
setA - setB 取两个集合的补集
setA ^ setB 取两个集合的对称差集(不同时存在的元素)
setA <= setB 判断 setA 是否是 setB 的子集
setA >= setB 判断 setA 是否是 setB 的超集
x in set 判断 x 是否在集合中
len(set) 返回集合中元素的个数
set.copy() 复制一个集合(浅拷贝), 返回复制后的集合

可迭代对象

  • 可迭代对象是指可以使用 for...in 循环的对象
  • 可以使用 iter() 函数转换为迭代器, 迭代器可以使用 next() 函数获取下一个值
  • list() 函数可以将可迭代对象或迭代器转换为列表
1
2
3
4
5
6
7
8
9
10
11
12
13
# 可迭代对象
lis = [1, 2, 3]
dic = {'name': 'xiaoyezi', 'age': 18}

# 迭代器
lis_iter = iter(lis)
dic_iter = iter(dic)

# 获取下一个值
print(next(lis_iter)) # 1
print(next(lis_iter)) # 2
print(next(lis_iter)) # 3
print(next(list_iter)) # StopIteration

元组

  • 元组由数个用逗号 , 隔开的值组成, 通常用小括号 () 括起来
  • 元组是不可变的, 不能修改元素的值
1
2
3
4
5
6
7
8
9
10
11
12
tup = 1, 2.0, '3', (4, 5)
print(tup[0]) # 1
print(tup[3][0]) # 4

empty = () # 空元组
single = 1, # 单个元素的元组, 逗号不能省略
print(len(empty)) # 0
print(len(single)) # 1

# 元组解构赋值
a, b, c, (d, e) = tup
print(a, b, c, d, e) # 1 2.0 3 4 5

内置函数

基础函数 描述 示例
len(iterable) 返回可迭代对象中元素的个数 len([1, 2, 3]) - 3
type(x) 返回 x 的类型 type(1) - <class 'int'>
id(x) 返回 x 的内存地址 id(1) - 140732674004992
isinstance(x, type) 判断 x 是否是 type 类型 isinstance(1, int) - True
print(x) 打印输出 print('Hello, world!') - Hello, world!
input(prompt) 接收用户输入 name = input('请输入你的名字: ')
range([start, ]end[, step]) 生成一个整数序列 list(range(5)) - [0, 1, 2, 3, 4]
enumerate(iterable) 返回可迭代对象的枚举对象 list(enumerate([1, 2, 3])) - [(0, 1), (1, 2), (2, 3)]
map(function, iterable) 返回可迭代对象中每个元素经过函数处理后的结果 list(map(lambda x: x * 2, [1, 2, 3])) - [2, 4, 6]
filter(function, iterable) 返回可迭代对象中满足条件的元素 list(filter(lambda x: x % 2 == 0, [1, 2, 3])) - [2]
sorted(iterable) 返回可迭代对象的排序列表 sorted([3, 2, 1]) - [1, 2, 3]
reversed(iterable) 返回可迭代对象的反向迭代器 list(reversed([1, 2, 3])) - [3, 2, 1]
  • filtermap 函数返回的是迭代器, 可以使用 list() 函数转换为列表
  • reversedsorted 不会改变原列表, 返回的是新列表, 类似于 JavaScripttoReversedtoSorted
类型转换函数 描述 示例
bool(x) 返回 x 的布尔值
bool(0/''/[]/{}/()/None) 返回 False, 其他返回 True
bool(0) - False
int(x) 返回 x 的整数值 int(1.5) - 1
float(x) 返回 x 的浮点数值 float(1) - 1.0
str(x) 返回 x 适合给人看的字符串 str(1) - ‘1’
repr(x) 返回 x 适合给解释器看的字符串 repr(1) - ‘1’
list(iterable) 返回可迭代对象的列表 list((1, 2, 3)) - [1, 2, 3]
tuple(iterable) 返回可迭代对象的元组 tuple([1, 2, 3]) - (1, 2, 3)
dict(iterable) 返回可迭代对象的字典 dict([('name', 'xiaoyezi'), ('age', 18)]) - {‘name’: ‘xiaoyezi’, ‘age’: 18}
set(iterable) 返回可迭代对象的集合 set([1, 2, 3]) - {1, 2, 3}
数学函数 描述 示例
abs(x) 返回 x 的绝对值 abs(-1) (1)
max(iterable) 返回可迭代对象中的最大值 max([1, 2, 3]) - 3
min(iterable) 返回可迭代对象中的最小值 min([1, 2, 3]) - 1
sum(iterable) 返回可迭代对象中的和 sum([1, 2, 3]) - 6
round(x[, n]) 返回 x 的四舍五入值 round(1.5) - 2
pow(x, y) 返回 xy 次幂, 等价于 x ** y pow(2, 3) - 8
divmod(x, y) 返回 x 除以 y 的商和余数 divmod(5, 2) - (2, 1)

运算符

运算符 描述 运算符 描述
+ -
* / 除, 总是返回浮点数
// 整除 (向下取整), 返回整数 % 取余
** not 逻辑非
and 逻辑与 or 逻辑或
== 等于 != 不等于
> 大于 < 小于
>= 大于等于 <= 小于等于
is 是(同一对象)

== 用于比较两个对象的值是否相等, is 用于比较两个对象的引用是否相等 (即 id(a) == id(b))

三元运算符

1
2
3
a = 1
b = 2
max = a if a > b else b

控制语句

注意, 缩进是 Python 语法的一部分, 缩进不一致会报错

if / elif / else

1
2
3
4
5
6
if age < 12:
print('小学生')
elif age < 18:
print('中学生')
else:
pass # pass 语句表示什么都不做, 用作块级作用域的占位符

for … in …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
for i in range(5):
print(i)
for i in dic:
print(dic[i])
for k, v in dic.items():
print(k, v)


# break / continue
for i in range(5):
if i == 3:
break # 跳出循环
if i == 2:
continue # 跳过本次循环
print(i)


# else, 如果循环在未执行 break 语句的情况下结束, 则执行 else 代码块
for i in range(5):
print(i)
else:
print('循环结束')

for i in range(5):
if i == 3:
break
print(i)
else:
print('这里不会执行')

推导式

  • 列表推导式是一种从一个列表创建另一个列表的方式, 类似于 JavaScriptmap 方法
  • 字典推导式和集合推导式类似, 只是使用大括号 {}
  • 生成器表达式和列表推导式基本相同, 只是使用小括号 () (参见生成器函数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 基础用法
lis = [i for i in range(5)] # [0, 1, 2, 3, 4]
lis = list(map(lambda i: i, range(5))) # [0, 1, 2, 3, 4]

# 添加条件
lis = [i for i in range(5) if i % 2 == 0] # [0, 2, 4]
lis = list(filter(lambda i: i % 2 == 0, range(5))) # [0, 2, 4]

# 嵌套循环
lis = [i for i in range(3) for j in range(2)] # [0, 0, 1, 1, 2, 2]
lis = list(map(lambda i: i, range(3) for j in range(2))) # [0, 0, 1, 1, 2, 2]

# 元组
lis = [(i, j) for i in range(3) for j in range(2)] # [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]

# 用来转制矩阵
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]
transpose = lambda matrix: [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transpose(matrix)) # [[1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12]]

# 字典推导式
dic = {i: i * i for i in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 生成器表达式
result = sum(i * i for i in range(5)) # 计算平方和
result = sum(x * y for x, y in zip([1, 2, 3], [4, 5, 6])) # 计算点积

while

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
i = 0
while i < 5:
print(i)
i += 1


# break / continue
i = 0
while i < 5:
if i == 3:
break # 跳出循环
if i == 2:
i += 1
continue # 跳过本次循环
print(i)
i += 1


# else, 如果循环在未执行 break 语句的情况下结束, 则执行 else 代码块
i = 0
while i < 5:
print(i)
i += 1
else:
print('循环结束')

i = 0
while i < 5:
if i == 3:
break
print(i)
i += 1
else:
print('这里不会执行')

match

match 类似于 JavaScriptswitch 语句, 但只会执行匹配的分支, 不会继续执行下面的分支 (更接近于 Rustmatch 语句)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def http_error(status):
match status:
case 400: # 单个条件
return "Bad request"
case 401 | 403: # 多个条件
return "Not allowed"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _: # 默认条件, 类似于 JavaScript 的 default
return "Something's wrong with the internet"


# 匹配元组
def match_point(point):
match point:
case (0, 0):
return "Origin"
case (0, y):
return f"Y-axis, {y}"
case (x, 0):
return f"X-axis, {x}"
case (x, y):
return f"({x}, {y})"


# 匹配类
class Point:
def __init__(self, x, y):
self.x = x
self.y = y

def match_point(point):
match point:
case Point(0, 0):
return "Origin"
case Point(0, y):
return f"Y-axis, {y}"
case Point(x, 0):
return f"X-axis, {x}"
case Point(x, y):
return f"({x}, {y})"


# as 关键字
def match_point(point):
match point:
case Point(0, 0):
return "Origin"
case Point(0, y) as p:
return f"Y-axis, {p.y}"
case Point(x, 0) as p:
return f"X-axis, {p.x}"
case Point(x, y) as p:
return f"({p.x}, {p.y})"

逻辑中断

1
2
3
4
5
6
7
8
9
10
11
12
13
# and
print(False and 'hello') # False
print(True and 'hello') # hello

# or
print(False or 'hello') # hello
print(True or 'hello') # True

# 注: 要在表达式内部赋值, 必须使用 := 而不是 = (为了避免把 == 写成 =)
a = 1
False or (a := 2)
True and (a := 3)
print(a) # 3

函数

  • Python 的函数总有返回值, 如果没有 return 语句, 则返回 None
  • 函数可以返回多个值 (通过元组类型)
  • 函数有自己的作用域, 内部变量不能被外部访问
1
2
3
4
5
6
# 定义函数
def add(arg1, arg2):
return arg1 + arg2

# 调用函数
print(add(1, 2)) # 3

默认参数

1
2
3
4
def add(arg1, arg2=1):
return arg1 + arg2

print(add(1)) # 2

注意, 默认参数的值只会在函数定义时计算一次, 如果默认参数是可变对象, 每次调用函数时都会共享这个对象

1
2
3
4
5
6
7
def add(arg1, arg2=[]):
arg2.append(arg1)
return arg2

print(add(1)) # [1]
print(add(2)) # [1, 2]
print(add(3)) # [1, 2, 3]

如果想要避免这种情况, 可以使用 None 作为默认参数 (因为 None 是不可变对象)

1
2
3
4
5
6
7
8
9
def add(arg1, arg2=None):
if arg2 is None:
arg2 = []
arg2.append(arg1)
return arg2

print(add(1)) # [1]
print(add(2)) # [2]
print(add(3)) # [3]

关键字参数

Python 的函数调用可以使用关键字参数, 这样可以不按照顺序传参; 但是, 关键字参数必须在位置参数之后

1
2
3
4
5
6
7
def add(arg1, arg2=1, arg3=2):
return arg1 + arg2 + arg3

print(add(1)) # 4
print(add(1, arg3=3)) # 5
print(add(arg3=3, arg2=2, arg1=1)) # 6
print(add(1, 2, 3)) # 6

动态参数

函数的参数列表末尾可以使用 *args**kwargs 接收不定数量的参数; *args 接受元组, 表示除了前面的参数外的其他位置参数; **kwargs 接受字典, 表示除了前面的参数外的其他关键字参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 只使用 *args
def fn(arg1, *args):
print(arg1)
print(args)

fn(1, 2, 3, 4) # 1 (2, 3, 4)

def fn(arg1, *args, arg2): # arg2 现在只能是关键字参数
print(arg1)
print(args)
print(arg2)

fn(1, 2, 3, 4, arg2=5) # 1 (2, 3, 4) 5


# 只使用 **kwargs
def fn(arg1, **kwargs):
print(arg1)
print(kwargs)

fn(1, a=2, b=3, c=4) # 1 {'a': 2, 'b': 3, 'c': 4}


# 同时使用 *args 和 **kwargs
# 注意: *args (位置参数) 必须在 **kwargs (关键字参数) 之前
def fn(arg1, *args, **kwargs):
print(arg1)
print(args)
print(kwargs)

fn(1, 2, 3, a=4, b=5) # 1 (2, 3) {'a': 4, 'b': 5}

特殊参数

默认情况下, 函数参数既可以是位置参数, 也可以是关键字参数; 但是, 可以使用 /* 来限制参数的类型:

  • / 之前的参数只能是位置参数
  • * 之后的参数只能是关键字参数
  • 两者之间的参数既可以是位置参数, 也可以是关键字参数
1
2
3
4
5
6
def fn(a, b, /, c, d, *, e, f):
print(a, b, c, d, e, f)

fn(1, 2, 3, 4, e=5, f=6) # 1 2 3 4 5 6
fn(1, 2, c=3, d=4, e=5, f=6) # 1 2 3 4 5 6
# 其他调用方式会报错

使用这种方式还可以避免关键字参数冲突:

1
2
3
4
5
6
7
8
9
10
def fn(a, **kwargs):
return 'a' in kwargs # 始终返回 False

print(fn(1, a=2)) # TypeError


def fn(a, /, **kwargs):
return 'a' in kwargs # 按预期执行

print(fn(1, a=2)) # True

展开运算符

解包, *** 运算符可以将列表或字典展开为位置参数或关键字参数

1
2
3
4
5
6
7
8
9
10
11
12
print(list(range(3, 6))) # [3, 4, 5]
args = [3, 6]
print(list(range(*args))) # [3, 4, 5]
print(list(range(*[3, 6]))) # [3, 4, 5]


def fn(a, b='b', c='c'):
print(a, b, c)

kwargs = {'b': 'B', 'c': 'C'}
fn('A', **kwargs) # A B C
fn(**{'a': 'A', 'b': 'B', 'c': 'C'}) # A B C

匿名函数

使用 lambda 关键字可以定义匿名函数. 匿名函数只能包含一个表达式, 不能包含多个语句. 语法为 lambda arguments: expression

1
2
add = lambda x, y: x + y
print(add(1, 2)) # 3

生成器函数

生成器函数使用 yield 关键字返回值, 每次调用 next() 函数时执行到 yield 语句, 并返回 yield 后的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 定义生成器函数
def gen():
yield 1
yield 2
yield 3


# 调用生成器函数, 返回可迭代对象
g = gen()
print(next(g)) # 1
print(next(g)) # 2
print(next(g)) # 3
print(next(g)) # StopIteration


# for...in 循环
for i in g:
print(i)
# 转换为列表
print(list(g)) # [1, 2, 3]

模块

  • 对于本地文件, 一个模块就是一个 xxx.py 文件, 其中 xxx 就是模块名
  • 对于本地目录, 如果目录中包含一个 __init__.py 文件, 则这个目录就是一个, 可以包含多个模块
  • 对于标准库, 它们是内置模块, 可以直接导入使用
  • 对于第三方库, 需要使用 pip 等工具安装后才能导入使用
  • 本地模块的优先级高于内置模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 导入模块
import math
print(math.sqrt(4)) # 2.0

# 导入模块中的部分函数
from math import sqrt, pow
print(sqrt(4)) # 2.0

# 导入模块中的所有函数 (不推荐)
from math import *
print(sqrt(4)) # 2.0

# 对模块或函数起别名
import math as m
from math import sqrt as s
print(m.sqrt(4)) # 2.0
print(s(4)) # 2.0

# 导入子模块
import os.path
print(os.path.abspath('demo.txt'))

Python 会在 sys.path 中的目录中查找模块, sys.path 是一个列表, 包含当前目录、环境变量 PYTHONPATHPython 安装目录等. 当前目录 指的是运行脚本的目录 (即当前工作目录), 而不是脚本所在的目录

相关技巧

1
2
3
4
5
6
7
8
9
10
# 以脚本方式运行模块
if __name__ == '__main__':
pass # 只有在当前模块作为脚本运行 (python xxx.py) 时才会执行

# 查看模块的属性和方法
import math
print(dir(math)) # ['__doc__', '__loader__', ..., 'sqrt', 'pow', ...]

# 直接调用 dir() 函数, 可以查看当前模块的所有属性和方法
print(dir()) # ['__annotations__', '__builtins__', ..., 'math', ...]

本地包

1
2
3
4
5
6
7
8
9
10
# 目录结构
.
├── main.py
└── mypackage
├── __init__.py
├── module1.py
├── module2.py
└─- subpackage
├── __init__.py
└── module3.py
1
2
3
4
# main.py
from mypackage import module1
from mypackage.module2 import func
from mypackage.subpackage import module3

__init__.py 文件

__init__.py 文件可以为空, 也可以包含模块的初始化代码, 会在包被导入时执行

1
2
3
4
5
6
7
8
9
10
# mypackage/__init__.py

# 定义 __all__ 变量可以手动指定可以被导入的模块
__all__ = ['module1', 'module2']

# 如果在 __init__.py 这定义了 module2, 则原有的 module2.py 文件会被覆盖
def module2():
print('module2')

# 可以在这个文件里自定义被导入时要执行的代码

相对导入

可以使用 ... 来导入同级和上级模块. 但根模块 (即 __main__) 不能使用相对导入

1
2
# mypackage/module1.py
from . import module2
1
2
# mypackage/subpackage/module3.py
from .. import module1

第三方包

  • pipPython 的官方包管理工具, 可以用来安装、卸载、升级 Python
  • PyPIPython 的包索引, 包含了大量的第三方包; 类似于npmjsr
  • venvPython 的虚拟环境工具, 可以创建独立的环境, 避免包冲突
  • conda 也是一个综合性的包管理/虚拟环境工具, 适用于科学计算和数据分析
  • pip install 将从 PyPI 安装包, 而 conda install 将从 Anaconda 仓库安装包; 后者还支持自动处理非 Python 依赖

pip

  • 对于全局 Python 环境中的 pip, 包将被安装到全局缓存中, 可以被所有项目访问
  • 对于 venv 中的 pip, 包将被安装到项目目录下, 只能被当前项目访问
  • 对于 conda 中的 pip, 包将被安装到对应 Python 版本的全局缓存中, 与 conda install 的安装位置一致, 但前者将不被 conda 管理
命令 描述
pip install package[==version]
pip install "package>=version"
安装包, 可以指定版本
pip install --upgrade package 升级包
pip install --upgrade pip 升级 pip
pip uninstall package 卸载包
pip list 查看已安装的包
pip freeze > requirements.txt 导出已安装的包到文件
pip install -r requirements.txt 从文件安装包

Anaconda

命令 描述
conda create -n xxx python=3.11 创建虚拟环境
conda activate xxx 激活虚拟环境
conda deactivate 退出虚拟环境
conda install package 安装包 (通过 Anaconda 仓库)
conda uninstall package 卸载包
conda list 查看已安装的包
conda update package 更新包
conda env list 查看所有虚拟环境

venv

推荐使用 VSCode 的交互式环境管理器, 可以方便地创建并激活虚拟环境, 并自动安装 pyproject.tomlrequirements.txt 中的依赖

打包和发布

参见官方文档. 本笔记使用 poetryuv 来进行相关操作 (点击跳转到相关章节)

  • sdist: 一个只包含纯 Python 代码的源码分发包, 形如 xxx.tar.gz
  • wheel: 一个包含编译后的扩展模块的分发包, 形如 xxx.whl
  • 构建工具: 用于构建包的工具, 如 setuptoolsflitpoetryuv

文件操作

内置函数 open() 用于打开文件, 返回一个文件对象. with open() as f 语句可以自动关闭文件

open() 的参数 默认值 描述
mode 'r' 打开文件的模式: r(只读)、w(只写)、a(追加)、b(二进制文件)、r+/w+/a+(读写)
encoding 'utf-8' 打开文件的编码格式; 如果是二进制文件, 则不能指定编码格式
文件对象方法 描述
f.read([size]) 读取文件, 可以指定读取的字节数
效果类似于 next(), 已读取到文件末尾时返回空字符串
f.readline() 读取一行
f.readlines() 读取所有行, 返回一个列表
f.write(str) 写入文件
f.writelines(lines) 将列表中的字符串写入文件
f.close() 关闭文件
f.tell() 返回文件指针的当前位置
f.seek(offset[, whence]) 移动文件指针到指定位置
whence 为参考点 (0: 文件开头, 1: 当前位置, 2: 文件末尾)
offset 为偏移量
1
2
3
4
5
6
7
8
9
10
11
12
# 读取文件
f = open('demo.txt', 'r+')
print(f.read())
f.write('Hello, world!\n')
f.close() # 直到关闭文件之前, 写入操作都可能没有在磁盘中生效


# 使用 with 语句
with open('demo.txt', 'r+') as f:
# 循环读取文件
for line in f:
print(line, end='') # end 定义打印时自动在行尾添加的字符, 默认为换行符

读取和解析 JSON

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import json


# 读取 JSON 文件
with open('data.json', 'r') as f:
data = json.load(f)
print(data)


# 写入 JSON 文件
data = {'name': 'xiaoyezi', 'age': 18}
with open('data.json', 'w') as f:
json.dump(data, f)


# 也可以单独使用 json 包
data = json.loads('{"name": "xiaoyezi", "age": 18}')
print(json.dumps(data))

错误处理

程序可以或被动或主动地抛出异常, 这些异常不一定会导致程序中断, 而是可以被捕获并处理. 这里列出了所有内置异常, BaseException 是所有异常的父类, 而 Exception 是所有非致命异常的父类; 用户自定义异常应该继承 Exception 类 (或其子类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# try 代码块用于捕获异常
try:
# 抛出异常
raise Exception('错误')
# except 代码块用于处理异常
except Exception as e:
print(e)
# 如果没有异常发生, 则执行 else
else:
pass
# 无论是否发生异常都会执行 finally
finally:
print('最后执行')


# 可以使用多个 except 代码块来处理不同类型的异常
try:
raise ValueError('错误')
except ValueError as e:
print(e)
except (TypeError, ZeroDivisionError) as e:
print(e)
except Exception as e:
print(e)
# 注意: 异常只会被第一个匹配的 except 代码块捕获, 除非使用 ExceptionGroup 和 except* (见下文)


# 可以使用 raise ... from ... 来保留原始异常
# 从而在异常链中保留原始异常的上下文
try:
func()
except Exception as e:
raise RuntimeError('函数调用失败') from e
# 也可以使用 raise ... from None 来禁用异常链

ExceptionGroup

ExceptionGroup 是一个用于捕获多个异常的类; 可以使用 except ExceptionGroup as e 来捕获多个异常或 except* XxxError as e 来捕获特定类型的异常并继续传播其他异常

1
2
3
4
5
6
7
8
9
10
11
12
13
try:
raise ExceptionGroup(
'Group error',
[
ValueError('Value error'),
TypeError('Type error'),
ZeroDivisionError('Zero division error')
]
)
except* ValueError as e:
print(e) # 'Group error (1 sub-exception)'
except* Exception as e:
print(e) # 'Group error (2 sub-exceptions)'

注意: except*except 不能同时使用

添加信息

可以使用 Exception.add_note() 方法来为错误添加额外的信息

1
2
3
4
5
6
try:
raise TypeError('bad type')
except Exception as e:
e.add_note('Add some information')
e.add_note('Add some more information')
raise e
1
2
3
4
5
6
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
raise TypeError('bad type')
TypeError: bad type
Add some information
Add some more information

自定义异常

请先了解的相关知识

1
2
3
4
5
6
7
8
9
10
11
class MyError(Exception):
# 构造函数
def __init__(self, message):
self.message = message
super().__init__(self.message)
# 重写 __str__ 方法, 用于打印异常信息
def __str__(self):
return f'MyError: {self.message}'
# 重写 __repr__ 方法, 用于调试
def __repr__(self):
return f'MyError({self.message})'

作用域

Python 的作用域通常为局部作用域 (类似于 JavaScript 的块级作用域), 但是可以使用 globalnonlocal 关键字来访问全局变量和外层变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def scope_test():
def do_local():
spam = '本地 spam'
def do_nonlocal():
nonlocal spam # 访问外层变量
spam = '非本地 spam'
def do_global():
global spam # 访问全局变量
spam = '全局 spam'

spam = '初始 spam'
do_local()
print('本地赋值后:', spam) # 初始 spam
do_nonlocal()
print('非本地赋值后:', spam) # 非本地 spam
do_global()
print('全局赋值后:', spam) # 非本地 spam


scope_test()
print('全局作用域:', spam) # 全局 spam

注意: globalnonlocal 仅在需要修改全局变量或外层变量时使用, 如果只是访问变量, 则不需要使用这两个关键字 (会自动由近到远地查找变量)

Class 是一种抽象的数据类型, 可以包含属性 Attribute方法 Method. 对象 Object 是类的实例, 可以访问类的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 定义类
class Person:
# 实例属性/静态属性
name = '小叶子'
# Python 的类没有私有属性, 但约定 __xxx 为私有属性
__sex = '男'
# 构造函数
def __init__(self, age, name=None):
self.age = age
name and (self.name := name) # 注意: 这里不会影响静态属性
# 实例方法
def say(self): # 第一个参数必须是 self
print(f'我叫{name}, 我今年{self.age}岁')
# 静态方法
@staticmethod # 这个是装饰器, 见后文
def intro():
print('这是一个人类')


# 访问静态属性和方法
print(Person.name) # 小叶子
Person.intro() # 这是一个人类


# 创建实例
p = Person(18)
# 访问实例属性和方法
print(p.age) # 18
p.say() # 我叫小叶子, 我今年18岁

继承

  • isinstance(obj, cls) 用于检查对象是否是类的实例
  • issubclass(sub, sup) 用于检查类是否是另一个类的子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 定义父类
class Animal:
def __init__(self, name):
self.name = name
def run(self):
print(f'{self.name} is running')


# 定义子类
class Dog(Animal):
def __init__(self, name, age):
super().__init__(name) # 调用父类的构造函数
self.age = age
def bark(self):
print(f'{self.name} is barking')


# 创建实例
d = Dog('旺财', 3)
d.run() # 旺财 is running
d.bark() # 旺财 is barking

可以在子类中重写父类的方法, 也可以在子类中调用父类的方法 super().method()

多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal:
def __init__(self, name):
self.name = name


class Person:
def __init__(self, age):
self.age = age


class Student(Animal, Person):
def __init__(self, name, age, grade):
Animal.__init__(self, name)
Person.__init__(self, age)
self.grade = grade

特殊方法

Python 中有一些特殊方法, 以双下划线开头和结尾, 用于实现类的特殊功能

方法 类型签名 描述
__init__ __init__(self, ...) -> None 构造函数
__repr__ __repr__(self) -> str 返回对象的字符串表示形式 (用于调试)
__str__ __str__(self) -> str 返回对象的字符串表示形式 (用于用户)
__iter__ __iter__(self) -> Iterator 返回一个迭代器对象
__next__ __next__(self) -> Any 返回迭代器的下一个元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person:
def __init__(self, hobbies):
self.hobbies = hobbies
self.index = 0

def __iter__(self):
return self

def __next__(self):
if self.index >= len(self.hobbies):
raise StopIteration
else:
self.index += 1
return self.hobbies[self.index - 1]


p = Person(['篮球', '足球', '乒乓球'])
for hobby in p:
print(hobby)

dataclass

dataclass 是一个装饰器, 可以自动为类添加 __init____repr____eq__ 等方法, 适用于定义数据类

1
2
3
4
5
6
7
8
9
10
from dataclasses import dataclass

@dataclass
class Person:
name: str
age: int

p = Person('小叶子', 18)
p = Person(age=18, name='小叶子')
print(p) # Person(name='小叶子', age=18)

getter & setter

实现了 __get____set__ 方法的类称为描述器, 可以用于实现属性的 gettersetter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 定义描述器
class Area:
def __get__(self, instance, owner):
return instance.width * instance.height
def __set__(self, instance, value):
raise AttributeError('不能直接设置面积')


# 定义类
class Rectangle:
area = Area()
def __init__(self, width, height):
self.width = width
self.height = height


# 创建实例
r = Rectangle(3, 4)
print(r.area) # 12
r.area = 10 # AttributeError: 不能直接设置面积

用作验证器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 定义验证器
class Number(Validator):
def __init__(self, minvalue=None, maxvalue=None):
self.minvalue = minvalue
self.maxvalue = maxvalue

def validate(self, value):
if not isinstance(value, (int, float)):
raise TypeError(f'Expected {value!r} to be an int or float')
if self.minvalue is not None and value < self.minvalue:
raise ValueError(
f'Expected {value!r} to be at least {self.minvalue!r}'
)
if self.maxvalue is not None and value > self.maxvalue:
raise ValueError(
f'Expected {value!r} to be no more than {self.maxvalue!r}'
)

# 定义类
class Rectangle:
width = Number(minvalue=0)
height = Number(minvalue=0)
def __init__(self, width, height):
self.width = width
self.height = height

# 创建实例
r = Rectangle(-3, 4) # ValueError: Expected -3 to be at least 0

Enum

Enum 是一个枚举类, 可以用于定义一组常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from enum import Enum

class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
# Python 的 Enum 还可以定义自定义方法
def from_str(cls, color):
return cls[color.upper()]

print(Color.RED) # Color.RED (__str__)
Color.RED # <Color.RED: 1> (__repr__)

print(Color.RED.name) # RED
print(Color.RED.value) # 1
print(Color(1)) # Color.RED
print(Color['RED']) # Color.RED
print(Color.from_str('red')) # Color.RED
print(list(Color)) # [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 3>]

唯一值 Enum

unique 是一个装饰器, 可以用于定义唯一值的枚举类

1
2
3
4
5
6
7
from enum import Enum, unique

@unique
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3

自动值 Enum

auto 函数可以用于自动为枚举值分配值

1
2
3
4
5
6
from enum import Enum, auto

class Color(Enum):
RED = auto()
GREEN = auto()
BLUE = auto()

类型注解

Python 不强制要求函数和变量的类型标注, 但是可以使用类型注解来提高代码的可读性、避免错误和提供类型提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 变量
s: str = 'xiaoyezi'
i: int = 18
b: bool # 可以不初始化
lis: list[int] = [1, 2, 3]
st: set[str] = {'a', 'b', 'c'}
dic: dict[str, int] = {'a': 1, 'b': 2, 'c': 3}
tup: tuple[int, str] = (1, 'a')
tup: tuple[int, ...] = (1, 2, 3)
a: Any = 1 # 任意类型, 不对其进行类型检查


# 函数表达式
from collections.abc import Callable # abc: Abstract Base Classes
fn: Callable[[int, int], int] = lambda x, y: x + y


# 联合类型 (Python 3.10+)
s: str | None = None
lis: list[int | str] = [1, 'a', 2, 'b']


# 函数
def add(arg1: int, arg2: int) -> int:
return arg1 + arg2
def register(callback: Callable[[int, int], int]) -> None:
print(callback(1, 2))
# 动态参数
def add(*args: int, **kwargs: str) -> int:
print(kwargs)
return sum(args)
# 生成器函数
from typing import Iterator
def gen(n: int) -> Iterator[int]:
for i in range(n):
yield i


# 类
class Person:
# 实例/静态属性
name: str
# 实例方法
def say(self, msg: str) -> None: # self 不需要标注类型
print(f'{self.name} say: {msg}')
# 构造函数
def __init__(self, name: str, age: int) -> None:
self.name = name
self.age = age # 可以进行类型推断
# 实例化
p: Person = Person('xiaoyezi', 18)
# 子类
class Student(Person):
def __init__(self, name: str, age: int, grade: int) -> None:
super().__init__(name, age)
self.grade = grade
# 子类类型
s: Student = Student('xiaoyezi', 18, 3)
s: Person = Student('xiaoyezi', 18, 3) # 不会报错


# 文件 (open() 函数返回值)
from typing import IO
def get_file_str(path: str) -> IO[str]:
return open(path, 'r')
def get_file_bytes(path: str) -> IO[bytes]:
return open(path, 'rb')

duck types

Python 是一种鸭子类型语言, 即只要对象实现了特定的方法, 就可以被视为特定的类型

  • typing.Mapping 是一个抽象基类, 用于表示只读的映射类型, 实现了 __getitem__
  • typing.MutableMapping 是一个抽象基类, 用于表示可变的映射类型, 实现了 __setitem____delitem__
1
2
3
4
5
6
7
8
from typing import Mapping, MutableMapping

def get_value(d: Mapping[str, int], key: str) -> int:
return d[key]
def set_value(d: MutableMapping[str, int], key: str, value: int) -> None:
d[key] = value
def del_value(d: MutableMapping[str, int], key: str) -> None:
del d[key]

类型别名

Python 3.12+ 中, 可以使用 type 关键字来定义类型别名

1
2
3
4
5
6
7
8
# 最新写法
type Vector = list[float]

# 兼容老版本
Vector = list[float]
# 或
from typing import TypeAlias
Vector: TypeAlias = list[float]

NewType

类型别名会把声明的别名和原类型视为同一类型, 但是 NewType 可以创建一个新的类型 (原类型的子类型), 用于增加类型安全性

1
2
3
4
from typing import NewType

UserId = NewType('UserId', int)
ProUserId = NewType('ProUserId', UserId)

字符串字面量类型

Python 不支持直接把字符串作为类型, 但是可以使用 Literal 来实现字符串字面量类型

1
2
3
4
5
from typing import Literal

def process_data(data: str, mode: Literal['r', 'w', 'a']) -> None:
with open('data.txt', mode) as f:
f.write(data)

泛型

Python 3.12+ 中, 加入了对泛型的语法支持 (即 [T])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def first[T](lis: list[T]) -> T:
return lis[0]
# 使用
print(first([1, 2, 3])) # 自动推断为 int


class Stack[T]:
def __init__(self):
self.items: list[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()
# 使用
s = Stack[int]()

老版本写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from typing import TypeVar, Generic


T = TypeVar('T')
def first(lis: list[T]) -> T:
return lis[0]


class Stack(Generic[T]):
def __init__(self):
self.items: list[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()

函数重载

Python 不支持函数重载, 但是可以使用 functools.singledispatch 装饰器来实现类似的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from functools import singledispatch

@singledispatch
def add(arg1, arg2):
raise NotImplementedError('Unsupported type')

@add.register
def _(arg1: int, arg2: int):
return arg1 + arg2

@add.register
def _(arg1: str, arg2: str):
return arg1 + arg2

print(add(1, 2)) # 3
print(add('a', 'b')) # ab

重载方法

functools.singledispatchmethod 装饰器用于重载类的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from functools import singledispatchmethod

class Adder:
@singledispatchmethod
def add(self, arg1, arg2):
raise NotImplementedError('Unsupported type')

@add.register
def _(self, arg1: int, arg2: int):
return arg1 + arg2

@add.register
def _(self, arg1: str, arg2: str):
return arg1 + arg2

a = Adder()
print(a.add(1, 2)) # 3
print(a.add('a', 'b')) # ab

或直接手动判断类型

1
2
3
4
5
6
7
8
class Adder:
def add(self, arg1, arg2):
if isinstance(arg1, int) and isinstance(arg2, int):
return arg1 + arg2
elif isinstance(arg1, str) and isinstance(arg2, str):
return arg1 + arg2
else:
raise NotImplementedError('Unsupported type')

装饰器 🚧

See more

⭐标准库

asyncio 🚧

asyncioPython 的异步 I/O 库, 可以用于编写异步程序, 非常类似于 JavaScriptPromise

threading 🚧

threadingPython 的多线程库, 可以用于创建线程; 但截止于 Python 3.13, Python 的多线程并不是真正的并行, 因为 GIL 会导致多线程在同一时间只有一个线程在执行 (仍类似于 JavaScriptPromise); 在 Python 3.13 中, 可选的 --disable-gil 选项可以禁用 GIL, 从而使之更接近于 Gogoroutine

multiprocessing 🚧

multiprocessingPython 的多进程库, 可以用于创建多个子进程, 从而实现并行执行, 类似于 JavaScriptWorker

queue 🚧

queuePython 的队列库, 可以用于实现线程安全的队列

⭐第三方工具

uv

uv 包含 poetrypipx 的所有功能, 但使用 Rust 编写, 速度更快 有望成为Python界的秦始皇

1
2
3
4
5
6
# 安装
brew install uv # 或参见 https://docs.astral.sh
# 卸载
brew uninstall uv # 或参见 https://docs.astral.sh
# 更新
brew upgrade # 或 uv self update
基础命令 说明
uv --version 查看版本 (常用于验证安装)
uv python install [3.12] 安装 Python, 也可使用系统已安装的 Python
强烈推荐 MacOS 用户使用 Homebrew 安装 Python
uv python uninstall [3.12] 卸载 Python
uv python list 查看系统中可用的所有 Python 版本
脚本相关命令 说明
uv init --script xxx.py --python 3.12 创建一个脚本文件 (会在顶部以文本形式声明 Python 版本)
uv run [--python 3.12] xxx.py 运行脚本 (如果检测到 pyproject.toml, 会自动安装依赖, 除非传入 --no-project 脚本有内联依赖)
uv run [--with pkg] [--with 'pkg>=version'] xxx.py 使用指定依赖运行脚本
uv add --script xxx.py ['pkg'] ['pkg>=version'] 内联式地安装依赖到脚本 (在脚本顶部添加依赖文本)
uv lock --script xxx.py 锁定脚本的依赖 (生成 xxx.py.lock 文件, 并在未来动态更新)
项目相关命令 说明
uv init [--python 3.12] [xxx] 创建一个新项目 (在当前目录或指定目录)
uv add [xxx] [xxx@^x.x.x] 添加依赖
uv remove xxx 移除依赖
uv lock --upgrade-package 升级所有依赖
uv sync 验证 pyproject.tomluv.lock 的一致性
uv run [xxx]/[xxx.py] 运行包或脚本
uv build 构建包, 输出到 dist 目录
uv publish 发布包 (需通过 --tokenUV_PUBLISH_TOKEN 传入 PyPI 令牌)
uv tree 查看依赖树
工具相关命令 说明
uv tool run xxx[@x.x.x]
uvx xxx[@x.x.x]
在临时隔离环境中运行工具
uv tool install xxx[@x.x.x] 安装工具
uv tool uninstall xxx 卸载工具
uv tool upgrade xxx 升级工具
uv tool upgrade --all 升级所有工具
uv tool list 查看已安装的工具
其他命令 说明
uv venv * 提供 venv 兼容的接口
uv pip * 提供 pippip-tools 兼容的接口
uv cache clean 清除所有缓存
uv cache prune 清除过时缓存
uv cache dir 查看缓存目录
uv tool dir 查看工具目录
uv python dir 查看 uvPython 安装目录

ruff

ruff 是一个快速的 Python 代码格式化和静态分析工具

1
2
3
uv add --dev ruff
uv run ruff check
uv run ruff check --fix

pipx

pipx 是一个用于管理 Python 包的工具, 可以在虚拟环境中运行包, 通常用于安装命令行工具

命令 描述
pipx install package [--python python3.xx] 安装包
pipx uninstall package 卸载包
pipx upgrade package 升级包
pipx list 查看已安装的包

poetry

poetry 是一个用于管理 Python 项目的工具, 可以创建新项目、添加依赖、构建包、发布包等

1
2
3
4
# 安装 poetry (macOS / Linux)
curl -sSL https://install.python-poetry.org | python3 -
# 卸载 poetry (macOS / Linux)
curl -sSL https://install.python-poetry.org | python3 - --uninstall
命令 说明
poetry --version 查看版本 (常用于验证安装)
poetry self update 更新 poetry
poetry new xxx [--name xxx] 创建新项目 (将创建项目目录和相关文件)
poetry init 交互式地初始化已存在项目
poetry install [--compile] 安装 poetry.lock (如果存在) 或 pyproject.toml 中的依赖
--compile 选项会在安装时即编译为字节码, 以加快首次运行速度
poetry sync 类似于 poetry install, 但会删除未在 pyproject.toml 中列出的依赖
poetry check 检查 poetry.lockpyproject.toml 的一致性
poetry update [xxx] 更新依赖, 修改 poetry.lock 文件
poetry add xxx[@xxx] 添加依赖
poetry remove xxx 移除依赖
poetry build 构建包
poetry config pypi-token.pypi <token>
poetry config pypi-token.testpypi <token>
配置 PyPI API 令牌
poetry publish [--build] 发布包 (需先配置 PyPI 令牌)
poetry run xxx
poetry run python xxx.py
poetry run pytest
自动判断当前虚拟环境并运行命令
  1. 运行 poetry new xxx 创建新项目
  2. 编辑 pyproject.toml 文件, 在 [project] 下添加 requires-python = ">=xxx" 指定 Python 版本
  3. 编写代码和测试, 有需要时通过 poetry add xxx 添加依赖
  4. 运行 poetry build 构建包
  5. 运行 poetry publish 发布包

依赖分组

poetry 可以把依赖分组 (类似于 JavaScriptdevDependenciesdependencies), 还可以把特定分组设置为可选依赖

命令 说明
poetry install --with <optional_group> 安装指定可选分组的依赖
poetry install --without <group> 安装时排除指定分组的依赖
poetry install --only <group> 只安装指定分组的依赖
poetry add xxx --group <group> 添加依赖到指定分组
poetry remove xxx --group <group> 从指定分组移除依赖
1
2
3
4
5
6
# 定义分组为可选
[tool.poetry.group.xxx]
optional = true

[tool.poetry.group.xxx.dependencies]
xxx = "*"

虚拟环境

poetry 会自动创建虚拟环境 (如果用户没有手动创建), 并在其中安装依赖

命令 说明
poetry env use [xxx/xxx | 3.12 | system] 切换虚拟环境
poetry env activate 输出激活当前虚拟环境的命令
eval $(poetry env activate) 激活当前虚拟环境 (macOS / Linux)
poetry env info 查看当前虚拟环境信息
poetry env list 列出与当前项目关联的所有虚拟环境
poetry env remove [xxx/xxx | 3.12] 删除现有虚拟环境
  • 标题: Python学习笔记
  • 作者: 小叶子
  • 创建于 : 2024-03-13 11:51:10
  • 更新于 : 2025-10-13 09:30:54
  • 链接: https://blog.leafyee.xyz/2024/03/13/Python/
  • 版权声明: 版权所有 © 小叶子,禁止转载。
评论