前言

本人有一定的Python基础,故记录的是自己不常用的或者是不太熟练的函数、模块

global和nonlocal关键字

  • 局部变量:是在函数或代码块内部定义的变量,只在该函数或块的作用域内可见
  • 全局变量:是在函数外部定义的变量,它在整个程序中都是可见的(但在函数中不能直接修改,除非使用global关键字)

注意:如果在函数中给一个变量赋值,那么这个变量会被默认认为是局部变量,即使函数外也有同名的局部变量

1
2
3
4
5
6
7
8
x = 5

def my_function():
x = 10 # 创建了一个局部变量 x
print(x) # 输出 10

my_function()
print(x) # 输出 5(全局变量没有被修改)

重点:如果希望在函数内部修改全局变量的值,必须显式使用global关键字

1
2
3
4
5
6
7
8
9
x = 5

def my_function():
global x
x = 10 # 修改全局变量 x
print(x) # 输出 10

my_function()
print(x) # 也输出 10

注意:nonlocal用于修改嵌套函数closure中的外层局部变量(不是全局变量)

1
2
3
4
5
6
7
8
9
def outer():
x = 5
def inner():
nonlocal x
x = 10
inner()
print(x) # 输出 10

outer()

如果没有nonlocalx = 10会被视为inner()的局部变量,外层变量不会被修改

类与初始化

一个类定义了一种数据结构,包括它的属性(变量)方法(函数)。使用类,你可以创建多个具有相同结构的对象

1
2
3
4
5
6
7
class 类名:
def __init__(self, 参数1, 参数2):
self.属性1 = 参数1
self.属性2 = 参数2

def 方法(self):
pass

__init__()方法详解

  • __init__()是一个构造函数,在类创建对象时被自动调用
  • self是对象自身的引用(表示当前对象本身),用于访问对象的属性和方法
1
2
3
4
5
6
7
8
9
10
11
class Person:
def __init__(self, name, age): # 初始化方法
self.name = name # 给对象添加 name 属性
self.age = age # 给对象添加 age 属性

def say_hello(self):
print(f"你好,我是 {self.name},我 {self.age} 岁了。")

# 创建对象
p1 = Person("张三", 30)
p1.say_hello() # 输出:你好,我是 张三,我 30 岁了。

后续可深入理解类的继承、多态、私有属性、类变量等内容

zip、lambda、map

zip():并行打包多个可迭代对象

  • 功能:将多个可迭代对象"打包"成一个 元组 的迭代器,适用于"配对"处理多个序列的情况
  • 基本语法:zip(iterable1, iterable2, ...)
  • 注意:zip()会在最短的序列长度处"截断"
1
2
3
4
5
names = ["张三", "李四", "王五"]
scores = [90, 85, 92]

for name, score in zip(names, scores):
print(f"{name} 的分数是 {score}")

lambda:匿名函数

  • 功能:快速创建小函数,常用于不需要复用的地方
  • 基本语法:lambda 参数1, 参数2: 表达式
1
2
3
4
students = [("张三", 90), ("李四", 85), ("王五", 92)]
# 按成绩排序
students_sorted = sorted(students, key=lambda x: x[1])
print(students_sorted)

map():对序列进行映射操作

  • 功能:对一个可迭代对象中每个元素执行相同的函数操作,返回一个map对象(可迭代)
  • 基于语法:map(function, iterable)
1
2
3
nums = [1, 2, 3, 4]
squares = list(map(lambda x: x**2, nums))
print(squares) # [1, 4, 9, 16]

组合使用案例:

1
2
3
4
5
a = [1, 2, 3]
b = [10, 20, 30]

result = list(map(lambda x: x[0] * x[1], zip(a, b)))
print(result) # [10, 40, 90]

浅拷贝和深拷贝

浅拷贝
  • 创建一个新的对象,但不复制嵌套结构的子对象
  • 原始对象中的子对象仍然是共享引用
深拷贝
  • 创建一个新的对象,并递归地复制所有嵌套的子对象
  • 原始对象与新对象完全独立

Python使用copy模块来处理浅拷贝和深拷贝:

1
2
3
4
5
6
7
8
9
10
import copy

a = [1, 2, [3, 4]]
b = copy.copy(a)

b[0] = 100 # 修改第一层,不影响 a
b[2][0] = 999 # 修改嵌套层,会影响 a!

print("a:", a) # [1, 2, [999, 4]]
print("b:", b) # [100, 2, [999, 4]]
1
2
3
4
5
6
7
8
9
10
import copy

a = [1, 2, [3, 4]]
b = copy.deepcopy(a)

b[0] = 100 # 修改第一层,不影响 a
b[2][0] = 999 # 修改嵌套层,也不影响 a!

print("a:", a) # [1, 2, [3, 4]]
print("b:", b) # [100, 2, [999, 4]]

注意:赋值(=)、浅拷贝copy()、深拷贝deepcopy()的区别

类型 是否创建新对象 是否复制子对象 是否共享引用
=赋值 是(完全共享)
copy.copy() 是(嵌套共享)
copy.deepcopy() 否(完全独立)

深拷贝对复杂结构或循环引用的数据结构可能比较慢且占内存

threading模块

threadingPython的标准库之一,用于实现多线程编程(即并发)。虽然Python由于GIL(全局解释器锁)的存在,在多线程CPU密集型任务中有局限,但对于I/O密集型任务(如文件读写、网络请求、数据库访问)来说,threading是非常有效的

  • 线程是程序执行的最小单位,是进程内的一个执行流。使用多线程可以同时处理多个任务,提高程序响应能力(尤其是等待IO的时候)

线程常用API

方法/属性 作用说明
Thread(target) 创建线程
start() 启动线程
join() 等待线程结束
current_thread() 获取当前线程对象
name 线程名称
is_alive() 判断线程是否还在运行
  • 线程同步:线程之间共享数据时可能出现数据竞争问题,这时需要使用锁(Lock)来保护临界区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threading

count = 0
lock = threading.Lock()

def worker():
global count
for _ in range(100000):
with lock:
count += 1

threads = [threading.Thread(target=worker) for _ in range(2)]
[t.start() for t in threads]
[t.join() for t in threads]

print(count) # 理想输出是200000

注意事项

  • PythonGIL会让多线程无法真正并行执行CPU密集型任务
  • 多线程适合处理IO密集型的任务(等待时间长,计算开销小)
  • 对共享资源操作一定要用锁!

multiprocessing模块

Pythonmultiprocessing模块用于多进程并行处理任务,是处理CPU密集型任务(如大规模计算、图像处理、模拟仿真)时的首选工具。它通过创建子进程来绕开PythonGIL,实现真正多核并行计算

进程池:Pool(批量任务处理):适合大量任务首发,自动管理进程数
1
2
3
4
5
6
7
8
9
10
11
from multiprocessing import Pool
import time

def square(n):
time.sleep(1)
return n * n

if __name__ == '__main__':
with Pool(processes=4) as pool:
results = pool.map(square, [1, 2, 3, 4, 5, 6])
print(results) # [1, 4, 9, 16, 25, 36]

特点:

  • map()会阻塞直到所有结果完成
  • apply_async()为非阻塞版本
进程间通信(IPC):多进程之间的数据不共享,需通过专用通道通信

使用Queue

1
2
3
4
5
6
7
8
9
10
11
from multiprocessing import Process, Queue

def worker(q):
q.put("你好,主进程!")

if __name__ == '__main__':
q = Queue()
p = Process(target=worker, args=(q,))
p.start()
print(q.get()) # 从子进程读取数据
p.join()

使用Pipe

1
2
3
4
5
6
7
8
9
10
11
12
from multiprocessing import Process, Pipe

def sender(conn):
conn.send("来自子进程的消息")
conn.close()

if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p = Process(target=sender, args=(child_conn,))
p.start()
print(parent_conn.recv()) # 接收消息
p.join()
共享数据:valueArray 用于进程间共享简单变量或数组
1
2
3
4
5
6
7
8
9
10
11
from multiprocessing import Process, Value

def add(val):
val.value += 10

if __name__ == '__main__':
num = Value('i', 100) # 'i' 表示整数
p = Process(target=add, args=(num,))
p.start()
p.join()
print(num.value) # 输出 110
进程锁:避免多个进程同时修改共享数据
1
2
3
4
5
6
7
8
9
10
from multiprocessing import Process, Lock

def task(lock, i):
with lock:
print(f"进程 {i} 正在写入")

if __name__ == '__main__':
lock = Lock()
for i in range(3):
Process(target=task, args=(lock, i)).start()

pickle模块

pickle模块是Python标准库中用于序列化的工具,主要用于将Python对象转换为字节流,以便存储到文件或通过网络传输,也可以从字节流还原成原始对象

  • 序列化:将Python对象转换为字节流(或字符串)
  • 反序列化:将字节流恢复为原来的Python对象
方法 说明
pickle.dump(obj, f) 将对象序列化写入文件
pickle.load(f) 从文件读取序列化内容并还原为对象
pickle.dumps(obj) 将对象序列化为字节串
pickle.loads(bytes) 从字节串反序列化为Python对象

注意:pickle.load()可以执行任意代码,有安全风险,容易被注入恶意代码

正则表达式

方法 说明
re.match(pattern, s) 从字符串开头匹配pattern
re.search(pattern, s) 搜索整个字符串,返回第一个匹配
re.findall(pattern, s) 返回所有匹配结果列表
re.finditer(pattern, s) 返回所有匹配的迭代器对象
re.sub(pattern, repl, s) 替换匹配到的内容为 repl
re.compile(pattern) 编译正则表达式,提高效率
1. 字符匹配
正则 含义
. 任意一个字符(不包括换行符)
\d 一个数字(0-9)
\D 非数字字符
\w 一个字母/数字/下划线
\W 非字母/数字/下划线
\s 空白字符(空格、换行、Tab)
\S 非空白字符
2. 量词(重复)
正则 含义
* 匹配0次或多次
+ 匹配1次或多次
? 匹配0次或1次
{n} 正好n次
{n,} 至少n次
{n,m} n 到 m 次之间
3. 边界匹配
正则 含义
^ 匹配字符串开头
$ 匹配字符串结尾
\b 单词边界
\B 非单词边界
4. 分组与或
正则 含义
(abc) 分组匹配 abc
` `
(?:abc) 非捕获分组
  • 多次使用同一正则时建议compile编译为模式对象,提高匹配速度
  • 字符串前加r""防止\被解释