Python 编程完整学习文档 # Python 编程完整学习文档 > **适用版本:Python 3.10+** > > 从零基础到高级特性,覆盖 25 个核心主题,250+ 可运行代码示例。每个知识点都配有可直接运行的代码示例、易错点提示和最佳实践建议。 ## 前置要求 | 项目 | 说明 | |------|------| | Python 版本 | 3.10+(推荐 3.12) | | 编辑器 | VS Code + Python 插件 / PyCharm | | 操作系统 | Windows / macOS / Linux | | 前置知识 | 无,从零开始 | --- ## 目录 <!-- more --> - [01 环境搭建与入门 ](#01-环境搭建与入门 )- [02 变量与数据类型 ](#02-变量与数据类型 )- [03 运算符详解 ](#03-运算符详解 )- [04 字符串操作 ](#04-字符串操作 )- [05 列表与元组 ](#05-列表与元组 )- [06 字典与集合 ](#06-字典与集合 )- [07 条件判断 ](#07-条件判断 )- [08 循环结构 ](#08-循环结构 )- [09 函数 ](#09-函数 )- [10 模块与包 ](#10-模块与包 )- [11 虚拟环境与包管理 ](#11-虚拟环境与包管理 )- [12 文件操作 ](#12-文件操作 )- [13 异常处理 ](#13-异常处理 )- [14 面向对象编程 ](#14-面向对象编程 )- [15 面向对象进阶 ](#15-面向对象进阶 )- [16 类型注解与静态分析 ](#16-类型注解与静态分析 )- [17 装饰器与闭包 ](#17-装饰器与闭包 )- [18 生成器与迭代器 ](#18-生成器与迭代器 )- [19 上下文管理器 ](#19-上下文管理器 )- [20 并发编程 ](#20-并发编程 )- [21 正则表达式 ](#21-正则表达式 )- [22 常用标准库 ](#22-常用标准库 )- [23 单元测试与调试 ](#23-单元测试与调试 )- [24 代码规范与项目工程化 ](#24-代码规范与项目工程化 )- [25 实战项目集 ](#25-实战项目集 )- [附录 A Python 3.10~3.13 新特性速查 ](#附录-a-python-3-103-13-新特性速查 )- [附录 B 常见陷阱与最佳实践 ](#附录-b-常见陷阱与最佳实践 )--- ## 01 环境搭建与入门 Python 是一门优雅、简洁、功能强大的通用编程语言,由 Guido van Rossum 于 1991 年创造。它的设计哲学强调**代码可读性** 和**简洁性** 。 ### 1.1 安装 Python ```bash # ===== macOS ===== # 方式一:Homebrew(推荐) brew install python@3.12 # 方式二:官方安装包 # 去 https://www.python.org/downloads/macos/ 下载 .pkg 安装 # ===== Ubuntu / Debian ===== sudo apt update sudo apt install -y python3.12 python3.12-venv python3-pip # ===== Fedora ===== sudo dnf install python3.12 # ===== Windows ===== # 去 https://www.python.org/downloads/ 下载安装包 # ⚠️ 安装时务必勾选 "Add Python to PATH" # 或者通过 winget 安装: winget install Python.Python.3.12 # ===== 验证安装 ===== python3 --version # Python 3.12.x python3 -m pip --version
1.2 第一个程序 """ 这是多行字符串,同时也是多行注释。 Python 没有专门的多行注释语法, 通常用三引号字符串来实现。 """ print ("Hello, World!" )print ("你好,Python!" )print ("Hello" , "World" , sep=", " ) print ("加载中" , end="..." ) print ("完成" ) print (""" ╔══════════════════════════╗ ║ Welcome to Python 3.12 ║ ╚══════════════════════════╝ """ )name = input ("请输入你的名字: " ) print (f"你好, {name} ! 欢迎学习 Python!" )
1.3 Python 的运行方式 python3 >>> 1 + 1 2 >>> "hello" .upper() 'HELLO' >>> exit () python3 hello.py python3 -c "print('hello')" python3 -m http.server 8080 pip install jupyter jupyter notebook
1.4 Python 之禅
核心原则摘录:
优美优于丑陋 (Beautiful is better than ugly)
明了优于隐晦 (Explicit is better than implicit)
简单优于复杂 (Simple is better than complex)
可读性很重要 (Readability counts)
做一件事应该有且仅有一种显而易见的方式
1.5 代码规范基础 if True : print ("缩进 4 个空格" ) if True : print ("再缩进 4 个空格" ) x = 1 y = 2 x = 1 ; y = 2 ; z = 3 total = (1 + 2 + 3 + 4 + 5 + 6 ) my_variable = 10 MY_CONSTANT = 3.14 my_function = lambda : 0 MyClass = type ("MyClass" , (), {})
02 变量与数据类型 Python 是动态类型 语言——变量不需要声明类型,赋值即创建,类型由值决定。
2.1 变量赋值 name = "Alice" age = 25 height = 1.68 is_student = True a, b, c = 1 , 2 , 3 x = y = z = 0 a, b = b, a count = 0 count += 1 print (type (name)) print (type (age)) print (type (is_student))
2.2 基本数据类型
类型
关键字
示例
说明
整数
int
42, -7, 0xFF
无大小限制
浮点数
float
3.14, 2.5e3, 1e-4
双精度浮点
布尔
bool
True, False
int 的子类
字符串
str
"hello", 'world'
不可变序列
空值
NoneType
None
表示”没有值”
复数
complex
3+4j
工程计算
big = 9999999999999999999999999999 print (big ** 2 ) print (0b1010 ) print (0o17 ) print (0xFF ) print (1_000_000 ) print (0.1 + 0.2 ) print (0.1 + 0.2 == 0.3 ) import mathprint (math.isclose(0.1 + 0.2 , 0.3 )) from decimal import Decimalprint (Decimal("0.1" ) + Decimal("0.2" ) == Decimal("0.3" )) print (True + True ) print (True * 10 ) print (isinstance (True , int ))
2.3 类型转换 result = 1 + 2.0 print (result, type (result)) s = "123" print (int (s)) print (float (s)) print (str (42 )) print (bool (0 )) print (bool ("" )) print (bool ([])) print (bool (None )) print (bool (42 )) print (bool ("hello" )) print (int (float ("3.14" )))
2.4 None 与 is 运算符 value = None if value is None : print ("value 是 None" ) if value is not None : print ("value 不是 None" ) x = [1 , 2 ] y = x z = [1 , 2 ] print (x == z) print (x is z) print (x is y)
03 运算符详解 3.1 算术运算符 a, b = 17 , 5 print (a + b) print (a - b) print (a * b) print (a / b) print (a // b) print (a % b) print (a ** b) print (-7 // 2 ) print (7 // -2 ) q, r = divmod (17 , 5 ) print (q, r) x = 10 x += 5 x -= 3 x *= 2 x //= 5 x **= 3
3.2 比较运算符 print (5 == 5 ) print (5 != 3 ) print (5 > 3 ) print (5 < 3 ) print (5 >= 5 ) print (5 <= 3 ) x = 5 print (1 < x < 10 ) print (0 <= x <= 100 ) print (1 < 2 < 3 < 4 < 5 )
3.3 逻辑运算符 print (True and False ) print (True or False ) print (not True ) print (0 or [] or "hello" or "world" ) print ("" or "default" ) print (1 and 2 and 3 ) print (1 and 0 and 3 ) name = user_input or "匿名用户" config = config or load_default_config() def side_effect (msg ): print (msg) return True False and side_effect("不会执行" ) True or side_effect("不会执行" )
3.4 身份运算符与成员运算符 a = [1 , 2 , 3 ] b = [1 , 2 , 3 ] c = a print (a == b) print (a is b) print (a is c) if a is None : pass if a is not None : pass print ("py" in "python" ) print (3 in [1 , 2 , 3 ]) print ("key" in {"key" : "val" })
3.5 位运算符 a, b = 0b1010 , 0b1100 print (bin (a & b)) print (bin (a | b)) print (bin (a ^ b)) print (bin (~a)) print (bin (a << 2 )) print (bin (a >> 1 )) READ = 0b100 WRITE = 0b010 EXECUTE = 0b001 permission = READ | WRITE print (permission & READ) print (permission & EXECUTE) permission |= EXECUTE print (bin (permission))
3.6 运算符优先级 从高到低:
优先级
运算符
说明
1
**
幂运算
2
+x, -x, ~x
一元正号、取反、按位取反
3
*, /, //, %
乘、除、整除、取余
4
+, -
加、减
5
<<, >>
移位
6
&
按位与
7
^
按位异或
8
|
按位或
9
==, !=, <, <=, >, >=, is, in
比较
10
not
逻辑非
11
and
逻辑与
12
or
逻辑或
💡 建议 :记不清优先级时,用括号明确表达意图。代码可读性永远比省几行代码更重要。
04 字符串操作 字符串是 Python 中最常用的数据类型。Python 字符串是不可变的 Unicode 序列 。
4.1 创建字符串 s1 = 'hello' s2 = "hello" s3 = '''多行 # 三引号:保留换行 字符串''' s4 = """也是 多行字符串""" s5 = "He said 'hello'" s6 = 'He said "hello"' s7 = "He said \"hello\"" path = r"C:\Users\test\new" regex = r"\d+\.\d+"
4.2 索引与切片 s = "Python" print (s[0 ]) print (s[-1 ]) print (s[0 :3 ]) print (s[2 :]) print (s[:4 ]) print (s[::2 ]) print (s[::-1 ]) print (s[0 :100 ])
4.3 f-string 格式化(推荐) name = "Alice" age = 25 pi = 3.14159 print (f"我叫 {name} ,今年 {age} 岁" )print (f"明年 {age + 1 } 岁" )print (f"大写: {name.upper()} " )print (f"圆周率: {pi:.2 f} " ) print (f"百分比: {0.856 :.1 %} " ) print (f"千分位: {1000000 :,} " ) print (f"二进制: {42 :b} " ) print (f"八进制: {42 :o} " ) print (f"十六进制: {255 :#x} " ) print (f"科学记数: {1234567.89 :.2 e} " ) print (f"{'左对齐' :<10 } !" ) print (f"{'右对齐' :>10 } !" ) print (f"{'居中' :^10 } !" ) print (f"{'填充' :*^10 } " ) x, y = 10 , 20 print (f"{x=} , {y=} , {x+y=} " )
4.4 常用方法 s = " Hello, World! " print (s.upper()) print (s.lower()) print (s.title()) print (s.swapcase()) print (s.capitalize()) print (s.strip()) print (s.lstrip()) print (s.rstrip()) print ("***hello***" .strip("*" )) s2 = "Hello, World!" print (s2.find("World" )) print (s2.find("xyz" )) print (s2.index("World" )) print (s2.count("l" )) print (s2.startswith("He" )) print (s2.endswith("!" )) print (s2.replace("World" , "Python" )) print (s2.replace("l" , "L" , 1 )) csv = "apple,banana,cherry" fruits = csv.split("," ) result = " | " .join(fruits) lines = "one\ntwo\nthree" .splitlines() print ("abc123" .isalnum()) print ("abc" .isalpha()) print ("123" .isdigit()) print (" " .isspace()) print ("Hello" .istitle()) print ("HELLO" .isupper())
4.5 字符串不可变性 s = "hello" s = "H" + s[1 :] s = s.replace("e" , "E" )
05 列表与元组 列表(list)是可变的有序序列 ,元组(tuple)是不可变的有序序列 。它们是 Python 中最常用的数据容器。
5.1 列表基础 fruits = ["apple" , "banana" , "cherry" ] mixed = [1 , "hello" , 3.14 , True , None , [1 , 2 ]] empty = [] nums = list (range (1 , 6 )) repeated = [0 ] * 5 print (len (fruits))
5.2 增删改查 fruits = ["apple" , "banana" , "cherry" ] fruits.append("date" ) fruits.insert(1 , "avocado" ) fruits.extend(["elderberry" ]) fruits += ["fig" ] fruits.remove("banana" ) popped = fruits.pop() popped = fruits.pop(0 ) del fruits[0 ] fruits.clear() fruits = ["apple" , "banana" , "cherry" ] fruits[0 ] = "apricot" fruits[1 :3 ] = ["blueberry" ] fruits = ["apple" , "banana" , "cherry" , "banana" ] print (fruits.index("banana" )) print (fruits.index("banana" , 2 )) print (fruits.count("banana" )) print ("apple" in fruits) print ("grape" not in fruits)
5.3 排序与反转 nums = [3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ] nums.sort() nums.sort(reverse=True ) words = ["banana" , "apple" , "cherry" , "date" ] words.sort(key=len ) words.sort(key=str .lower) original = [3 , 1 , 4 , 1 , 5 ] new_sorted = sorted (original) print (original) print (new_sorted) nums = [1 , 2 , 3 ] nums.reverse() print (list (reversed (nums)))
5.4 列表推导式 squares = [x**2 for x in range (10 )] evens = [x for x in range (20 ) if x % 2 == 0 ] matrix = [[1 , 2 , 3 ], [4 , 5 , 6 ], [7 , 8 , 9 ]] flat = [num for row in matrix for num in row] labels = ["偶" if x % 2 == 0 else "奇" for x in range (5 )] words = ["hello" , "WORLD" , "Python" ] lower_words = [w.lower() for w in words] long_words = [w for w in words if len (w) > 4 ] matrix_t = [[row[i] for row in matrix] for i in range (3 )]
5.5 元组 point = (3 , 4 ) single = (42 ,) t = tuple ([1 , 2 , 3 ]) print (point[0 ]) print (point[1 :]) print (len (point)) print (point.count(3 )) print (point.index(4 )) x, y = point print (x, y) first, *rest = [1 , 2 , 3 , 4 , 5 ] first, *middle, last = (1 , 2 , 3 , 4 , 5 ) from collections import namedtuplePoint = namedtuple("Point" , ["x" , "y" ]) p = Point(3 , 4 ) print (p.x, p.y) print (p[0 ], p[1 ]) from typing import NamedTupleclass Student (NamedTuple ): name: str age: int grade: float = 4.0 s = Student("Alice" , 20 ) print (s.name, s.grade)
5.6 list vs tuple 选择指南
特性
list
tuple
可变
✅
❌
可作为字典的键
❌
✅
可放入集合
❌
✅
内存占用
较大
较小
创建速度
较慢
较快
适用场景
需要增删改的集合
固定结构的记录
💡 经验法则 :如果数据不需要修改,用 tuple。函数返回多个值时用 tuple。当做字典的键时用 tuple。
06 字典与集合 6.1 字典基础 user = { "name" : "Alice" , "age" : 25 , "city" : "Beijing" , "skills" : ["Python" , "SQL" ], } d1 = dict (name="Alice" , age=25 ) d2 = dict ([("name" , "Alice" ), ("age" , 25 )]) d3 = dict .fromkeys(["a" , "b" , "c" ], 0 ) print (user["name" ]) print (user.get("phone" )) print (user.get("phone" , "无" ))
6.2 增删改查 user = {"name" : "Alice" , "age" : 25 } user["email" ] = "alice@example.com" user["age" ] = 26 user.setdefault("city" , "Beijing" ) user.setdefault("age" , 30 ) user.update({"age" : 27 , "phone" : "123" }) del user["phone" ] email = user.pop("email" ) user.pop("xxx" , None ) last = user.popitem() user.clear() user = {"name" : "Alice" , "age" : 25 , "city" : "Beijing" } for key in user: print (key) for value in user.values(): print (value) for key, value in user.items(): print (f"{key} : {value} " ) print ("name" in user) print ("phone" not in user)
6.3 字典推导式 squares = {x: x**2 for x in range (6 )} even_sq = {x: x**2 for x in range (10 ) if x % 2 == 0 } original = {"a" : 1 , "b" : 2 , "c" : 3 } flipped = {v: k for k, v in original.items()} keys = ["name" , "age" , "city" ] values = ["Alice" , 25 , "Beijing" ] person = dict (zip (keys, values))
6.4 字典合并(Python 3.9+) d1 = {"a" : 1 , "b" : 2 } d2 = {"b" : 3 , "c" : 4 } merged = d1 | d2 d1 |= d2
6.5 集合 s1 = {1 , 2 , 3 , 4 , 5 } s2 = set ([3 , 4 , 5 , 6 , 7 ]) s3 = {1 , 2 , 2 , 3 , 3 , 3 } empty_set = set () empty_dict = {} print (s1 | s2) print (s1 & s2) print (s1 - s2) print (s1 ^ s2) s1.add(10 ) s1.remove(1 ) s1.discard(99 ) s1.pop() print (3 in s1)print ({1 , 2 }.issubset({1 , 2 , 3 })) print ({1 , 2 , 3 }.issuperset({1 , 2 })) print ({1 , 2 }.isdisjoint({3 , 4 })) sq_set = {x**2 for x in range (10 )}
⚠️ 集合元素必须是可哈希的(不可变类型)
列表、字典、集合不能放入集合中。元组可以(前提是元素也都是不可变的)。
07 条件判断 7.1 if / elif / else score = 85 if score >= 90 : grade = "A" elif score >= 80 : grade = "B" elif score >= 70 : grade = "C" elif score >= 60 : grade = "D" else : grade = "F" print (f"成绩: {score} ,等级: {grade} " )
7.2 三元表达式 age = 20 status = "成年" if age >= 18 else "未成年" print (status) score = 85 level = ("优秀" if score >= 90 else "良好" if score >= 80 else "及格" if score >= 60 else "不及格" )
7.3 match / case(Python 3.10+) def handle_response (status_code: int ): match status_code: case 200 : return "成功" case 400 : return "请求错误" case 401 : return "未授权" case 404 : return "未找到" case 500 : return "服务器错误" case _: return f"未知状态码: {status_code} " print (handle_response(404 )) match status_code: case 200 | 201 | 204 : return "成功" case 400 | 401 | 403 | 404 : return "客户端错误" match command: case ["quit" ]: print ("退出" ) case ["go" , direction]: print (f"走向 {direction} " ) case ["pick" , *items]: print (f"拾取 {items} " )
7.4 真值判断 items = [] if not items: print ("列表为空" ) name = "" if not name: print ("名字为空" ) data = None if data is None : print ("数据为空" )
08 循环结构 8.1 for 循环 for fruit in ["apple" , "banana" , "cherry" ]: print (fruit) for i in range (5 ): print (i) for i in range (2 , 10 ): pass for i in range (0 , 10 , 2 ): pass for i in range (10 , 0 , -1 ): pass for index, fruit in enumerate (["apple" , "banana" ]): print (f"{index} : {fruit} " ) for i, line in enumerate (lines, start=1 ): print (f"第 {i} 行: {line} " ) names = ["Alice" , "Bob" , "Charlie" ] ages = [25 , 30 , 35 ] cities = ["Beijing" , "Shanghai" , "Shenzhen" ] for name, age, city in zip (names, ages, cities): print (f"{name} , {age} , {city} " ) from itertools import zip_longestfor a, b in zip_longest([1 , 2 , 3 ], ["a" , "b" ], fillvalue="?" ): print (a, b)
8.2 while 循环 count = 0 while count < 5 : print (count) count += 1 while True : text = input ("输入 quit 退出: " ) if text == "quit" : break for i in range (10 ): if i % 2 == 0 : continue print (i) for n in range (2 , 20 ): for i in range (2 , n): if n % i == 0 : break else : print (f"{n} 是质数" )
8.3 循环技巧 for i, (name, age) in enumerate (zip (names, ages)): print (f"{i} : {name} is {age} " ) for item in reversed ([1 , 2 , 3 ]): print (item) for item in sorted ([3 , 1 , 2 ]): print (item) d = {"b" : 2 , "a" : 1 , "c" : 3 } for k, v in sorted (d.items()): print (k, v) squares = [x**2 for x in range (10 )]
09 函数 9.1 定义与调用 def greet (name: str ) -> str : """向指定的人打招呼。 Args: name: 人名 Returns: 打招呼的字符串 """ return f"你好, {name} !" print (greet("Alice" )) def say_hello (): print ("Hello" ) result = say_hello() print (result) def min_max (nums: list ) -> tuple : return min (nums), max (nums) lo, hi = min_max([3 , 1 , 4 , 1 , 5 ]) print (lo, hi)
9.2 参数类型 def power (base, exp ): return base ** exp power(2 , 10 ) def power (base, exp=2 ): return base ** exp power(3 ) power(2 , 10 ) def connect (host, port, *, ssl=False , timeout=30 ): pass connect("localhost" , 8080 , ssl=True ) def sum_all (*args ): print (type (args)) return sum (args) sum_all(1 , 2 , 3 , 4 ) def show_info (**kwargs ): for key, value in kwargs.items(): print (f"{key} : {value} " ) show_info(name="Alice" , age=25 ) def func (a, b=10 , *args, c, d=20 , **kwargs ): pass
9.3 参数解包 def add (a, b, c ): return a + b + c nums = [1 , 2 , 3 ] print (add(*nums)) params = {"a" : 1 , "b" : 2 , "c" : 3 } print (add(**params))
9.4 Lambda 匿名函数 square = lambda x: x ** 2 print (square(5 )) nums = [1 , 2 , 3 , 4 , 5 ] doubled = list (map (lambda x: x * 2 , nums)) evens = list (filter (lambda x: x % 2 == 0 , nums)) from functools import reducetotal = reduce(lambda a, b: a + b, nums) students = [("Alice" , 85 ), ("Bob" , 92 ), ("Eve" , 78 )] by_score = sorted (students, key=lambda s: s[1 ], reverse=True )
9.5 闭包与作用域 x = "global" def outer (): x = "enclosing" def inner (): x = "local" print (x) inner() outer() def make_counter (): count = 0 def counter (): nonlocal count count += 1 return count return counter c = make_counter() print (c()) print (c()) print (c())
⚠️ 可变默认参数陷阱
def bad_append (item, lst=[] ): lst.append(item) return lst print (bad_append(1 )) print (bad_append(2 )) def good_append (item, lst=None ): if lst is None : lst = [] lst.append(item) return lst
9.6 递归 def factorial (n: int ) -> int : if n <= 1 : return 1 return n * factorial(n - 1 ) print (factorial(5 )) from functools import lru_cache@lru_cache(maxsize=None ) def fib (n: int ) -> int : if n < 2 : return n return fib(n - 1 ) + fib(n - 2 ) print (fib(100 )) import syssys.setrecursionlimit(5000 )
10 模块与包 10.1 导入方式 import mathprint (math.sqrt(16 )) from math import sqrt, piprint (sqrt(25 )) print (pi) import numpy as npfrom collections import defaultdict as dd
10.2 创建模块和包 myproject/ ├── main.py ├── utils.py # 单文件模块 └── mypackage/ # 包目录 ├── __init__.py # 标识为包 ├── math_tools.py ├── string_tools.py └── sub_package/ # 子包 └── __init__.py
def clamp (value, min_val, max_val ): return max (min_val, min (value, max_val)) if __name__ == "__main__" : print (clamp(15 , 0 , 10 ))
from utils import clampfrom mypackage.math_tools import some_function
11 虚拟环境与包管理 11.1 venv 虚拟环境 python3 -m venv .venv source .venv/bin/activate .venv\Scripts\activate deactivate
11.2 pip 包管理 pip install requests pip install requests==2.31.0 pip install "requests>=2.28,<3.0" pip install --upgrade requests pip uninstall requests pip install -r requirements.txt pip freeze > requirements.txt pip list pip show requests pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
11.3 现代工具链 pip install uv uv pip install requests uv venv uv pip compile requirements.in pip install poetry poetry new myproject poetry add requests poetry install poetry build
12 文件操作 12.1 文本文件读写 with open ("data.txt" , "w" , encoding="utf-8" ) as f: f.write("第一行\n" ) f.write("第二行\n" ) f.writelines(["第三行\n" , "第四行\n" ]) with open ("data.txt" , "a" , encoding="utf-8" ) as f: f.write("追加内容\n" ) with open ("data.txt" , "r" , encoding="utf-8" ) as f: content = f.read() with open ("data.txt" , "r" , encoding="utf-8" ) as f: lines = f.readlines() with open ("data.txt" , "r" , encoding="utf-8" ) as f: for line in f: print (line.strip())
模式
说明
文件不存在
文件已存在
'r'
只读
报错
—
'w'
写入
创建
清空
'a'
追加
创建
保留
'x'
创建
创建
报错
'r+'
读写
报错
保留
'rb'
二进制读
报错
—
12.2 pathlib 路径操作(推荐) from pathlib import Pathp = Path("data" ) / "output" / "result.txt" print (p) print (p.parent) p.exists() p.is_file() p.is_dir() p.name p.stem p.suffix p.parent p.parent.mkdir(parents=True , exist_ok=True ) for f in Path("." ).glob("*.py" ): print (f) for f in Path("." ).rglob("*.json" ): print (f) Path("note.txt" ).write_text("hello" , encoding="utf-8" ) content = Path("note.txt" ).read_text(encoding="utf-8" ) Path("data.bin" ).write_bytes(b"\x00\x01" ) data = Path("data.bin" ).read_bytes()
12.3 CSV 操作 import csvwith open ("data.csv" , "w" , newline="" , encoding="utf-8" ) as f: writer = csv.writer(f) writer.writerow(["姓名" , "年龄" , "城市" ]) writer.writerow(["Alice" , 25 , "Beijing" ]) writer.writerow(["Bob" , 30 , "Shanghai" ]) with open ("data.csv" , "w" , newline="" , encoding="utf-8" ) as f: fieldnames = ["姓名" , "年龄" , "城市" ] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerow({"姓名" : "Alice" , "年龄" : 25 , "城市" : "Beijing" }) with open ("data.csv" , "r" , encoding="utf-8" ) as f: reader = csv.DictReader(f) for row in reader: print (row["姓名" ], row["城市" ])
12.4 JSON 操作 import jsondata = {"name" : "Alice" , "age" : 25 , "skills" : ["Python" , "SQL" ]} json_str = json.dumps(data, ensure_ascii=False , indent=2 ) print (json_str)obj = json.loads(json_str) with open ("data.json" , "w" , encoding="utf-8" ) as f: json.dump(data, f, ensure_ascii=False , indent=2 ) with open ("data.json" , "r" , encoding="utf-8" ) as f: loaded = json.load(f) class User : def __init__ (self, name, age ): self .name = name self .age = age def user_encoder (obj ): if isinstance (obj, User): return {"name" : obj.name, "age" : obj.age} raise TypeError(f"Object of type {type (obj)} is not JSON serializable" ) user = User("Alice" , 25 ) print (json.dumps(user, default=user_encoder))
13 异常处理 13.1 try / except / finally try : result = 10 / 0 except ZeroDivisionError: print ("不能除以零!" ) except (TypeError, ValueError) as e: print (f"错误: {e} " ) except Exception as e: print (f"未知错误: {e} " ) else : print (f"结果: {result} " ) finally : print ("处理完成" )
13.2 常见异常类型
异常
说明
示例
ValueError
值不合法
int("abc")
TypeError
类型错误
"1" + 2
KeyError
字典键不存在
d["missing"]
IndexError
索引越界
[1,2][5]
FileNotFoundError
文件不存在
open("no.txt")
AttributeError
属性不存在
None.upper()
ImportError
导入失败
import noexist
StopIteration
迭代器耗尽
next(iter([]))
RecursionError
递归过深
无限递归
MemoryError
内存不足
[0] * 10**10
RuntimeError
运行时错误
修改迭代中的集合
13.3 自定义异常 class AppError (Exception ): """应用基础异常""" pass class InsufficientFundsError (AppError ): def __init__ (self, balance, amount ): self .balance = balance self .amount = amount super ().__init__( f"余额不足: 余额 {balance} ,需要 {amount} " ) class InvalidAccountError (AppError ): pass def withdraw (balance, amount ): if amount <= 0 : raise ValueError("取款金额必须为正数" ) if amount > balance: raise InsufficientFundsError(balance, amount) return balance - amount try : withdraw(100 , 200 ) except InsufficientFundsError as e: print (e) raise AppError("交易失败" ) from e
13.4 实用模式 try : value = d[key] except KeyError: value = default if key in d: value = d[key] else : value = default from contextlib import suppresswith suppress(FileNotFoundError): os.remove("temp.txt" )
14 面向对象编程 14.1 类与实例 class Dog : """一只狗的类""" species = "Canis lupus" def __init__ (self, name: str , age: int ): self .name = name self .age = age def bark (self ) -> str : return f"{self.name} says: Woof!" def __repr__ (self ): return f"Dog('{self.name} ', {self.age} )" def __str__ (self ): return f"{self.name} ({self.age} 岁)" dog = Dog("Buddy" , 3 ) print (dog.bark()) print (dog.species) print (repr (dog)) print (str (dog))
14.2 继承 class Animal : def __init__ (self, name: str ): self .name = name def speak (self ) -> str : raise NotImplementedError("子类必须实现 speak" ) def info (self ) -> str : return f"{self.__class__.__name__} : {self.name} " class Cat (Animal ): def __init__ (self, name: str , indoor: bool = True ): super ().__init__(name) self .indoor = indoor def speak (self ) -> str : return f"{self.name} : Meow!" class Duck (Animal ): def speak (self ) -> str : return f"{self.name} : Quack!" class Dog (Animal ): def __init__ (self, name: str , breed: str ): super ().__init__(name) self .breed = breed def speak (self ) -> str : return f"{self.name} : Woof!" for animal in [Cat("Kitty" ), Duck("Donald" ), Dog("Buddy" , "金毛" )]: print (animal.speak()) print (animal.info()) c = Cat("Kitty" ) print (isinstance (c, Cat)) print (isinstance (c, Animal)) print (issubclass (Cat, Animal)) print (issubclass (Cat, object ))
14.3 访问控制 class Person : def __init__ (self, name, age, password ): self .name = name self ._title = "Mr." self .__password = password self ._age = age @property def age (self ): return self ._age @age.setter def age (self, value ): if not isinstance (value, int ): raise TypeError("年龄必须是整数" ) if value < 0 or value > 150 : raise ValueError("年龄必须在 0~150 之间" ) self ._age = value @property def is_adult (self ) -> bool : return self ._age >= 18 p = Person("Alice" , 25 , "secret" ) p.age = 30 print (p.age) print (p.is_adult) print (p._title) print (p._Person__password)
14.4 类方法与静态方法 class Date : def __init__ (self, year, month, day ): self .year = year self .month = month self .day = day def format (self ) -> str : return f"{self.year} -{self.month:02d} -{self.day:02d} " @classmethod def from_string (cls, date_str: str ): year, month, day = map (int , date_str.split("-" )) return cls(year, month, day) @staticmethod def is_valid (date_str: str ) -> bool : try : parts = date_str.split("-" ) return len (parts) == 3 and all (p.isdigit() for p in parts) except Exception: return False d1 = Date(2024 , 1 , 15 ) d2 = Date.from_string("2024-06-01" ) print (d1.format ()) print (Date.is_valid("2024-01-15" ))
14.5 魔法方法 class Vector : def __init__ (self, x: float , y: float ): self .x = x self .y = y def __repr__ (self ): return f"Vector({self.x} , {self.y} )" def __str__ (self ): return f"({self.x} , {self.y} )" def __add__ (self, other ): return Vector(self .x + other.x, self .y + other.y) def __sub__ (self, other ): return Vector(self .x - other.x, self .y - other.y) def __mul__ (self, scalar ): return Vector(self .x * scalar, self .y * scalar) def __rmul__ (self, scalar ): return self .__mul__(scalar) def __eq__ (self, other ): return self .x == other.x and self .y == other.y def __abs__ (self ): return (self .x**2 + self .y**2 ) ** 0.5 def __bool__ (self ): return self .x != 0 or self .y != 0 def __len__ (self ): return 2 v1 = Vector(3 , 4 ) v2 = Vector(1 , 2 ) print (v1 + v2) print (v1 * 3 ) print (3 * v1) print (abs (v1)) print (v1 == Vector(3 , 4 ))
常用魔法方法速查:
方法
触发方式
说明
__init__
Class()
初始化
__repr__
repr(obj)
开发者字符串
__str__
print(obj)
用户字符串
__len__
len(obj)
长度
__getitem__
obj[key]
索引访问
__setitem__
obj[key] = val
索引设置
__delitem__
del obj[key]
索引删除
__contains__
x in obj
成员判断
__iter__
for x in obj
迭代
__next__
next(obj)
取下一个值
__add__
a + b
加法
__eq__
a == b
相等判断
__lt__
a < b
小于
__call__
obj()
使实例可调用
__enter__ / __exit__
with obj:
上下文管理器
15 面向对象进阶 15.1 dataclass(Python 3.7+) from dataclasses import dataclass, field@dataclass class Point : x: float y: float def distance (self ) -> float : return (self .x**2 + self .y**2 ) ** 0.5 p = Point(3 , 4 ) print (p) print (p.distance()) @dataclass(order=True ) class Student : sort_index: float = field(init=False , repr =False ) name: str grade: float age: int = 18 def __post_init__ (self ): self .sort_index = self .grade students = [ Student("Alice" , 85 ), Student("Bob" , 92 ), Student("Eve" , 78 ), ] print (sorted (students)) @dataclass(frozen=True ) class Config : host: str = "localhost" port: int = 8080
15.2 抽象基类 from abc import ABC, abstractmethodclass Shape (ABC ): @abstractmethod def area (self ) -> float : pass @abstractmethod def perimeter (self ) -> float : pass def describe (self ) -> str : return f"{self.__class__.__name__} : area={self.area():.2 f} " class Circle (Shape ): def __init__ (self, radius: float ): self .radius = radius def area (self ) -> float : import math return math.pi * self .radius ** 2 def perimeter (self ) -> float : import math return 2 * math.pi * self .radius c = Circle(5 ) print (c.describe())
15.3 多重继承与 MRO class A : def greet (self ): return "A" class B (A ): def greet (self ): return "B" class C (A ): def greet (self ): return "C" class D (B, C): pass d = D() print (d.greet()) print (D.__mro__)class Base : def __init__ (self ): print ("Base" ) class Left (Base ): def __init__ (self ): super ().__init__() print ("Left" ) class Right (Base ): def __init__ (self ): super ().__init__() print ("Right" ) class Child (Left, Right): def __init__ (self ): super ().__init__() print ("Child" ) Child()
15.4 Slots class Point : __slots__ = ("x" , "y" ) def __init__ (self, x, y ): self .x = x self .y = y p = Point(3 , 4 ) print (p.x, p.y)
15.5 描述符 class PositiveNumber : def __set_name__ (self, owner, name ): self .name = name def __get__ (self, obj, objtype=None ): if obj is None : return self return getattr (obj, f"_{self.name} " , 0 ) def __set__ (self, obj, value ): if value < 0 : raise ValueError(f"{self.name} 必须为非负数" ) setattr (obj, f"_{self.name} " , value) class Account : balance = PositiveNumber() def __init__ (self, balance ): self .balance = balance acc = Account(100 ) print (acc.balance)
16 类型注解与静态分析 Python 是动态类型语言,但 3.5+ 支持类型注解,配合 mypy 等工具进行静态类型检查。
16.1 基础类型注解 name: str = "Alice" age: int = 25 height: float = 1.68 is_active: bool = True def greet (name: str , times: int = 1 ) -> str : return f"Hello, {name} ! " * times from typing import Optional , Union , Any def find_user (user_id: int ) -> Optional [str ]: """可能返回 None""" ... def process (value: Union [int , str ] ) -> None : """接受 int 或 str""" ... def flexible (x: Any ) -> Any : """接受任意类型""" ...
16.2 容器类型注解 from typing import List , Dict , Tuple , Set , Sequence , Mappingnames: List [str ] = ["Alice" , "Bob" ] scores: Dict [str , int ] = {"Alice" : 95 , "Bob" : 87 } point: Tuple [int , int ] = (3 , 4 ) tags: Set [str ] = {"python" , "coding" } names: list [str ] = ["Alice" , "Bob" ] scores: dict [str , int ] = {"Alice" : 95 } matrix: list [list [int ]] = [[1 , 2 ], [3 , 4 ]] data: dict [str , list [int ]] = {"a" : [1 , 2 ]} def first (items: Sequence [int ] ) -> int : return items[0 ]
16.3 高级类型 from typing import ( Callable , Iterator, Generator, Literal , TypeVar, Generic , Protocol, TypeAlias ) Handler = Callable [[str , int ], bool ] def on_event (handler: Handler ) -> None : ...def set_mode (mode: Literal ["read" , "write" , "append" ] ) -> None : ...T = TypeVar("T" ) def first (items: list [T] ) -> T: return items[0 ] class Stack (Generic [T]): def __init__ (self ) -> None : 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 ] = Stack() s.push(1 ) def first [T](items: list [T]) -> T: return items[0 ] class Stack [T]: def __init__ (self ) -> None : self ._items: list [T] = []
16.4 Protocol(结构化子类型) from typing import Protocolclass Drawable (Protocol ): def draw (self ) -> str : ... @property def color (self ) -> str : ... class Circle : def __init__ (self, color: str ): self ._color = color def draw (self ) -> str : return "Drawing circle" @property def color (self ) -> str : return self ._color def render (obj: Drawable ) -> None : print (obj.draw(), obj.color) render(Circle("red" ))
16.5 使用 mypy 进行静态检查 pip install mypy mypy my_script.py --strict
17 装饰器与闭包 17.1 闭包回顾 def make_multiplier (factor ): def multiply (x ): return x * factor return multiply double = make_multiplier(2 ) triple = make_multiplier(3 ) print (double(5 )) print (triple(5 ))
17.2 装饰器基础 import timefrom functools import wrapsdef timer (func ): """计时装饰器""" @wraps(func ) def wrapper (*args, **kwargs ): start = time.perf_counter() result = func(*args, **kwargs) elapsed = time.perf_counter() - start print (f"⏱ {func.__name__} 耗时: {elapsed:.4 f} s" ) return result return wrapper @timer def compute (): time.sleep(0.5 ) return "done" result = compute() print (result) print (compute.__name__)
17.3 带参数的装饰器 def retry (max_retries=3 , delay=1 ): """重试装饰器""" def decorator (func ): @wraps(func ) def wrapper (*args, **kwargs ): for attempt in range (1 , max_retries + 1 ): try : return func(*args, **kwargs) except Exception as e: print (f"第 {attempt} 次尝试失败: {e} " ) if attempt < max_retries: time.sleep(delay) else : raise return wrapper return decorator @retry(max_retries=3 , delay=0.5 ) def fetch_data (): import random if random.random() < 0.7 : raise ConnectionError("网络错误" ) return "数据"
17.4 类装饰器 class CountCalls : def __init__ (self, func ): self .func = func self .count = 0 def __call__ (self, *args, **kwargs ): self .count += 1 print (f"{self.func.__name__} 被调用了 {self.count} 次" ) return self .func(*args, **kwargs) @CountCalls def say_hello (): print ("Hello!" ) say_hello() say_hello()
17.5 内置装饰器 class MyClass : @staticmethod def utility (x, y ): return x + y @classmethod def create (cls ): return cls() @property def name (self ): return self ._name
17.6 常用标准库装饰器 from functools import lru_cache, singledispatch@lru_cache(maxsize=128 ) def fibonacci (n ): if n < 2 : return n return fibonacci(n - 1 ) + fibonacci(n - 2 ) @singledispatch def process (data ): raise NotImplementedError(f"不支持 {type (data)} " ) @process.register(int ) def _ (data ): print (f"处理整数: {data} " ) @process.register(str ) def _ (data ): print (f"处理字符串: {data} " ) @process.register(list ) def _ (data ): print (f"处理列表: {data} " ) process(42 ) process("hello" ) process([1 , 2 , 3 ])
18 生成器与迭代器 18.1 迭代器协议 class Countdown : def __init__ (self, start ): self .start = start def __iter__ (self ): self .current = self .start return self def __next__ (self ): if self .current <= 0 : raise StopIteration self .current -= 1 return self .current + 1 for num in Countdown(5 ): print (num, end=" " ) nums = iter ([1 , 2 , 3 ]) print (next (nums)) print (next (nums)) print (next (nums))
18.2 生成器函数 def fibonacci (n ): a, b = 0 , 1 for _ in range (n): yield a a, b = b, a + b for num in fibonacci(10 ): print (num, end=" " ) fib_list = list (fibonacci(10 )) def flatten (nested ): for item in nested: if isinstance (item, (list , tuple )): yield from flatten(item) else : yield item data = [1 , [2 , 3 ], [4 , [5 , 6 ]]] print (list (flatten(data)))
18.3 生成器表达式 squares_list = [x**2 for x in range (1000000 )] squares_gen = (x**2 for x in range (1000000 )) total = sum (x**2 for x in range (1000000 )) max_val = max (x**2 for x in range (1000 ))
💡 生成器 vs 列表
列表 :所有元素立即生成,存在内存中
生成器 :每次调用 next() 才产生下一个值
数据量大或不需要全部元素时,优先用生成器
18.4 send() 和 close() def accumulator (): total = 0 while True : value = yield total if value is None : break total += value gen = accumulator() next (gen) print (gen.send(10 )) print (gen.send(20 )) print (gen.send(5 )) gen.close()
19 上下文管理器 19.1 基本用法 with open ("data.txt" , "w" ) as f: f.write("hello" )
19.2 类实现 import timeclass Timer : def __enter__ (self ): self .start = time.perf_counter() return self def __exit__ (self, exc_type, exc_val, exc_tb ): self .elapsed = time.perf_counter() - self .start print (f"⏱ 耗时: {self.elapsed:.4 f} s" ) return False with Timer() as t: time.sleep(0.3 ) print (f"代码块耗时: {t.elapsed:.4 f} s" )
19.3 contextmanager 装饰器 from contextlib import contextmanager@contextmanager def timer (): start = time.perf_counter() yield print (f"⏱ 耗时: {time.perf_counter() - start:.4 f} s" ) with timer(): time.sleep(0.3 ) import os@contextmanager def change_dir (path ): old_dir = os.getcwd() os.chdir(path) try : yield finally : os.chdir(old_dir) with change_dir("/tmp" ): print (os.getcwd()) print (os.getcwd())
19.4 ExitStack from contextlib import ExitStackwith ExitStack() as stack: files = [ stack.enter_context(open (f"file{i} .txt" , "w" )) for i in range (3 ) ] for f in files: f.write("hello" )
20 并发编程 20.1 多线程(threading) import threadingimport timedef worker (name: str , delay: float ): print (f"[{name} ] 开始" ) time.sleep(delay) print (f"[{name} ] 完成" ) t1 = threading.Thread(target=worker, args=("A" , 2 )) t2 = threading.Thread(target=worker, args=("B" , 1 )) t1.start() t2.start() t1.join() t2.join() print ("全部完成" )lock = threading.Lock() counter = 0 def safe_increment (): global counter for _ in range (100000 ): with lock: counter += 1
⚠️ GIL 限制
Python 的全局解释器锁(GIL)确保同一时刻只有一个线程执行 Python 字节码。多线程适合 I/O 密集型任务 (网络请求、文件读写),不适合 CPU 密集型任务 (大量计算)。
20.2 多进程(multiprocessing) from multiprocessing import Process, Poolimport osdef heavy_task (n ): """CPU 密集型任务""" total = sum (i * i for i in range (n)) return total if __name__ == "__main__" : p = Process(target=heavy_task, args=(10_000_000 ,)) p.start() p.join() with Pool(4 ) as pool: results = pool.map (heavy_task, [10_000_000 ] * 4 ) print (results)
20.3 异步编程(asyncio) import asyncioasync def fetch_data (name: str , delay: float ) -> str : print (f"[{name} ] 开始获取数据..." ) await asyncio.sleep(delay) print (f"[{name} ] 数据获取完成" ) return f"{name} _data" async def main (): results = await asyncio.gather( fetch_data("API-1" , 2 ), fetch_data("API-2" , 1 ), fetch_data("API-3" , 1.5 ), ) print (f"结果: {results} " ) asyncio.run(main())
20.4 异步上下文管理器与迭代器 import asyncioimport aiofiles async def read_file (): async with aiofiles.open ("data.txt" , "r" ) as f: content = await f.read() print (content) async def async_range (n ): for i in range (n): await asyncio.sleep(0.1 ) yield i async def main (): async for num in async_range(5 ): print (num)
20.5 concurrent.futures from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutorwith ThreadPoolExecutor(max_workers=4 ) as executor: futures = [executor.submit(download_url, url) for url in urls] results = [f.result() for f in futures] with ProcessPoolExecutor(max_workers=4 ) as executor: results = list (executor.map (heavy_computation, data_list))
并发方案选择:
场景
推荐方案
原因
I/O 密集型
asyncio / ThreadPoolExecutor
GIL 不影响 I/O 等待
CPU 密集型
multiprocessing / ProcessPoolExecutor
绕过 GIL,利用多核
高并发网络
asyncio + aiohttp
单线程高并发,低开销
简单并行
concurrent.futures
接口统一,使用简单
21 正则表达式 21.1 基础语法 import retext = "我的邮箱是 alice@example.com,电话是 138-0000-1234"
21.2 常用函数 text = "Hello 123 World 456 Python789" m = re.search(r'\d+' , text) print (m.group()) print (m.start()) print (m.end()) m = re.match (r'\d+' , text) print (m) m = re.fullmatch(r'\d+' , "12345" ) nums = re.findall(r'\d+' , text) print (nums) for m in re.finditer(r'\d+' , text): print (f"'{m.group()} ' at {m.start()} -{m.end()} " ) cleaned = re.sub(r'\d+' , '*' , text) print (cleaned) parts = re.split(r'[,;\s]+' , "hello, world; python java" ) print (parts)
21.3 分组捕获 text = "2024-01-15" m = re.match (r'(\d{4})-(\d{2})-(\d{2})' , text) if m: print (m.group(0 )) print (m.group(1 )) print (m.group(2 )) print (m.group(3 )) m = re.match (r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})' , text) if m: print (m.group('year' )) print (m.groupdict()) m = re.findall(r'(?:https?://)?([\w.]+)' , "https://www.example.com http://test.org" ) prices = "苹果 $3.50,香蕉 ¥5.00" dollar_prices = re.findall(r'\$(\d+\.\d{2})' , prices) print (dollar_prices)
21.4 编译正则(提高性能) EMAIL_PATTERN = re.compile (r'[\w.+-]+@[\w-]+\.[\w.]+' ) PHONE_PATTERN = re.compile (r'1[3-9]\d{9}' ) text = "联系我 alice@example.com 或 13812345678" emails = EMAIL_PATTERN.findall(text) phones = PHONE_PATTERN.findall(text) re.IGNORECASE re.MULTILINE re.DOTALL re.VERBOSE pattern = re.compile (r""" (?P<year>\d{4}) # 年 [-/.] # 分隔符 (?P<month>\d{2}) # 月 [-/.] # 分隔符 (?P<day>\d{2}) # 日 """ , re.VERBOSE)
21.5 常用正则模板 r'[\w.+-]+@[\w-]+\.[\w.]+' r'1[3-9]\d{9}' r'\b(?:\d{1,3}\.){3}\d{1,3}\b' r'https?://[\w./-]+' r'\d{17}[\dXx]' r'[\u4e00-\u9fff]+' r'<[^>]+>'
22 常用标准库 22.1 collections — 高级容器 from collections import Counter, defaultdict, deque, OrderedDict, ChainMapwords = "hello world hello python hello world" .split() count = Counter(words) print (count.most_common(2 )) print (count["hello" ]) count.update(["hello" , "new" ]) dd = defaultdict(list ) for word in ["apple" , "banana" , "avocado" , "blueberry" ]: dd[word[0 ]].append(word) dq = deque([1 , 2 , 3 ], maxlen=5 ) dq.appendleft(0 ) dq.pop() dq.rotate(2 ) defaults = {"color" : "red" , "size" : "medium" } custom = {"color" : "blue" } config = ChainMap(custom, defaults) print (config["color" ]) print (config["size" ])
import itertoolsfor i in itertools.count(10 ): if i > 14 : break print (i, end=" " ) for item in itertools.cycle(["A" , "B" ]): if ...: break list (itertools.repeat("hello" , 3 )) list (itertools.permutations("ABC" , 2 ))list (itertools.combinations("ABC" , 2 ))list (itertools.product("AB" , repeat=2 ))list (itertools.chain([1 ,2 ], [3 ,4 ], [5 ])) data = [("A" , 1 ), ("A" , 2 ), ("B" , 3 ), ("B" , 4 )] for key, group in itertools.groupby(data, key=lambda x: x[0 ]): print (key, list (group)) list (itertools.islice(range (100 ), 5 , 15 ))list (itertools.accumulate([1 , 2 , 3 , 4 ])) list (itertools.pairwise("ABC" ))
22.3 datetime — 日期时间 from datetime import datetime, date, time, timedeltanow = datetime.now() today = date.today() print (now.strftime("%Y-%m-%d %H:%M:%S" ))print (now.strftime("%Y年%m月%d日 %A" ))dt = datetime.strptime("2024-01-15 14:30" , "%Y-%m-%d %H:%M" ) tomorrow = now + timedelta(days=1 ) last_week = now - timedelta(weeks=1 ) two_hours_later = now + timedelta(hours=2 ) diff = datetime(2024 , 12 , 31 ) - datetime(2024 , 1 , 1 ) print (diff.days) timestamp = now.timestamp() from_timestamp = datetime.fromtimestamp(timestamp)
22.4 os 和 sys import osimport sysos.getcwd() os.listdir("." ) os.makedirs("a/b/c" , exist_ok=True ) os.rename("old.txt" , "new.txt" ) os.remove("file.txt" ) os.environ["HOME" ] os.path.join("a" , "b" , "c" ) sys.argv sys.version sys.path sys.exit(0 ) sys.getsizeof(obj)
22.5 logging — 日志 import logginglogging.basicConfig( level=logging.DEBUG, format ="%(asctime)s [%(levelname)s] %(message)s" , datefmt="%Y-%m-%d %H:%M:%S" , handlers=[ logging.FileHandler("app.log" , encoding="utf-8" ), logging.StreamHandler(), ] ) logger = logging.getLogger(__name__) logger.debug("调试信息" ) logger.info("普通信息" ) logger.warning("警告" ) logger.error("错误" ) logger.critical("严重错误" ) try : 1 / 0 except Exception: logger.exception("发生异常" )
22.6 hashlib — 哈希摘要 import hashlibmd5 = hashlib.md5("hello" .encode()).hexdigest() print (md5) sha256 = hashlib.sha256("hello" .encode()).hexdigest() print (sha256)def file_hash (filepath, algorithm="sha256" ): h = hashlib.new(algorithm) with open (filepath, "rb" ) as f: while chunk := f.read(8192 ): h.update(chunk) return h.hexdigest()
22.7 random — 随机数 import randomrandom.randint(1 , 10 ) random.random() random.uniform(1.0 , 10.0 ) random.choice(["a" , "b" , "c" ]) random.sample(range (100 ), 5 ) random.shuffle(my_list) import secretstoken = secrets.token_hex(16 ) secure_num = secrets.randbelow(100 )
22.8 argparse — 命令行参数 import argparseparser = argparse.ArgumentParser(description="文件处理工具" ) parser.add_argument("input" , help ="输入文件路径" ) parser.add_argument("-o" , "--output" , default="output.txt" , help ="输出路径" ) parser.add_argument("-n" , "--number" , type =int , default=10 , help ="数量" ) parser.add_argument("-v" , "--verbose" , action="store_true" , help ="详细模式" ) args = parser.parse_args() if args.verbose: print (f"处理文件: {args.input } " ) print (f"数量: {args.number} " )
python tool.py input.txt -o result.txt -n 20 -v
23 单元测试与调试 23.1 unittest import unittestdef add (a, b ): return a + b def divide (a, b ): if b == 0 : raise ValueError("除数不能为零" ) return a / b class TestMath (unittest.TestCase): def setUp (self ): """每个测试方法前执行""" self .data = [1 , 2 , 3 , 4 , 5 ] def test_add (self ): self .assertEqual(add(1 , 2 ), 3 ) self .assertEqual(add(-1 , 1 ), 0 ) self .assertEqual(add(0 , 0 ), 0 ) def test_divide (self ): self .assertAlmostEqual(divide(10 , 3 ), 3.3333 , places=3 ) self .assertRaises(ValueError, divide, 10 , 0 ) with self .assertRaises(ValueError): divide(10 , 0 ) def test_list_operations (self ): self .assertIn(3 , self .data) self .assertNotIn(6 , self .data) self .assertTrue(all (x > 0 for x in self .data)) def tearDown (self ): """每个测试方法后执行""" pass if __name__ == "__main__" : unittest.main()
23.2 pytest(推荐) def add (a, b ): return a + b def test_add_basic (): assert add(1 , 2 ) == 3 def test_add_negative (): assert add(-1 , -1 ) == -2 def test_add_zero (): assert add(0 , 0 ) == 0 import pytest@pytest.mark.parametrize("a,b,expected" , [ (1 , 2 , 3 ), (0 , 0 , 0 ), (-1 , 1 , 0 ), (100 , -100 , 0 ), ] )def test_add_parametrize (a, b, expected ): assert add(a, b) == expected @pytest.fixture def sample_list (): return [1 , 2 , 3 , 4 , 5 ] def test_sum (sample_list ): assert sum (sample_list) == 15 def test_max (sample_list ): assert max (sample_list) == 5 def test_divide_by_zero (): with pytest.raises(ValueError, match ="除数不能为零" ): divide(10 , 0 )
pytest test_math.py -v pytest --tb=short pytest -k "add"
23.3 调试技巧 print (f"DEBUG: {variable=} " )assert x > 0 , f"x 应该大于 0,实际值为 {x} " def problematic_function (): x = 10 breakpoint () y = x + 20 return y from icecream import icic(variable) import logginglogging.debug(f"变量值: {variable} " )
23.4 doctest def factorial (n ): """ 计算 n 的阶乘。 >>> factorial(0) 1 >>> factorial(1) 1 >>> factorial(5) 120 >>> factorial(10) 3628800 """ if n <= 1 : return 1 return n * factorial(n - 1 ) if __name__ == "__main__" : import doctest doctest.testmod()
24 代码规范与项目工程化 24.1 PEP 8 代码风格 variable_name = 10 CONSTANT_VALUE = 3.14 def function_name (): pass class ClassName : _private = True __mangled = True import osimport sysimport requestsimport numpy as npfrom mypackage import module
24.2 格式化工具 pip install black black my_script.py black --line-length 88 . pip install ruff ruff check . ruff check --fix . ruff format . pip install isort isort . pip install mypy mypy --strict .
24.3 项目结构 myproject/ ├── pyproject.toml # 项目配置(取代 setup.py) ├── README.md ├── LICENSE ├── .gitignore ├── .env # 环境变量(不提交到 git) │ ├── src/ │ └── mypackage/ │ ├── __init__.py │ ├── core.py │ ├── utils.py │ └── models/ │ ├── __init__.py │ └── user.py │ ├── tests/ │ ├── __init__.py │ ├── test_core.py │ └── test_utils.py │ ├── docs/ │ └── api.md │ └── scripts/ └── setup.sh
24.4 pyproject.toml [project] name = "myproject" version = "0.1.0" description = "My awesome project" readme = "README.md" requires-python = ">=3.10" dependencies = [ "requests>=2.28" , "pydantic>=2.0" , ] [project.optional-dependencies] dev = [ "pytest>=7.0" , "black" , "ruff" , "mypy" , ] [tool.black] line-length = 88 [tool.ruff] line-length = 88 select = ["E" , "F" , "I" , "UP" , "B" ][tool.mypy] python_version = "3.12" strict = true [tool.pytest.ini_options] testpaths = ["tests" ]
24.5 Docstring 规范(Google 风格) def fetch_user (user_id: int , include_posts: bool = False ) -> dict : """从 API 获取用户信息。 根据用户 ID 从远程 API 获取用户详情。可选择是否包含 用户的帖子列表。 Args: user_id: 用户的唯一标识符,必须为正整数。 include_posts: 是否包含用户的帖子列表,默认 False。 Returns: 包含用户信息的字典,格式如下: { "id": int, "name": str, "email": str, "posts": list[dict] (如果 include_posts 为 True) } Raises: ValueError: 当 user_id 不是正整数时。 ConnectionError: 当 API 请求失败时。 Examples: >>> user = fetch_user(1) >>> print(user["name"]) 'Alice' >>> user = fetch_user(1, include_posts=True) >>> len(user["posts"]) 5 """ ...
25 实战项目集 25.1 命令行待办清单 """命令行待办清单 —— 综合练习 覆盖:文件 I/O、JSON、异常处理、函数、控制流 """ import jsonfrom pathlib import Pathfrom datetime import datetimeDATA_FILE = Path("todos.json" ) def load_todos () -> list [dict ]: if DATA_FILE.exists(): return json.loads(DATA_FILE.read_text(encoding="utf-8" )) return [] def save_todos (todos: list [dict ] ) -> None : DATA_FILE.write_text( json.dumps(todos, ensure_ascii=False , indent=2 ), encoding="utf-8" ) def add_todo (todos: list [dict ], text: str ) -> None : todos.append({ "text" : text, "done" : False , "created" : datetime.now().isoformat() }) save_todos(todos) def list_todos (todos: list [dict ] ) -> None : if not todos: print (" (暂无待办事项)" ) return for i, t in enumerate (todos, 1 ): mark = "✓" if t["done" ] else "○" print (f" {i} . [{mark} ] {t['text' ]} " ) def main () -> None : todos = load_todos() print ("📋 待办清单(输入 help 查看命令)" ) commands = { "list" : "显示所有待办" , "add <text>" : "添加待办" , "done <num>" : "标记完成" , "delete <num>" : "删除待办" , "quit" : "退出程序" , } while True : try : cmd = input ("> " ).strip() except (EOFError, KeyboardInterrupt): break if not cmd: continue if cmd == "quit" : break elif cmd == "help" : for k, v in commands.items(): print (f" {k:<15 } {v} " ) elif cmd == "list" : list_todos(todos) elif cmd.startswith("add " ): add_todo(todos, cmd[4 :]) print (" 已添加 ✓" ) elif cmd.startswith("done " ): try : idx = int (cmd[5 :]) - 1 todos[idx]["done" ] = True save_todos(todos) print (" 已完成 ✓" ) except (IndexError, ValueError): print (" 无效编号" ) elif cmd.startswith("delete " ): try : idx = int (cmd[7 :]) - 1 todos.pop(idx) save_todos(todos) print (" 已删除 ✓" ) except (IndexError, ValueError): print (" 无效编号" ) else : print (" 未知命令,输入 help 查看帮助" ) if __name__ == "__main__" : main()
25.2 Web 爬虫基础 """简单网页爬虫 —— requests + BeautifulSoup 覆盖:HTTP 请求、HTML 解析、异常处理、数据存储 """ import jsonimport timeimport loggingfrom pathlib import Pathfrom dataclasses import dataclass, asdictimport requestsfrom bs4 import BeautifulSoup logging.basicConfig(level=logging.INFO, format ="%(asctime)s %(message)s" ) logger = logging.getLogger(__name__) @dataclass class Quote : text: str author: str tags: list [str ] def scrape_quotes (url: str , max_pages: int = 5 ) -> list [Quote]: quotes = [] for page in range (1 , max_pages + 1 ): page_url = f"{url} /page/{page} /" logger.info(f"正在爬取: {page_url} " ) try : resp = requests.get(page_url, timeout=10 ) resp.raise_for_status() except requests.RequestException as e: logger.error(f"请求失败: {e} " ) break soup = BeautifulSoup(resp.text, "html.parser" ) quote_divs = soup.find_all("div" , class_="quote" ) if not quote_divs: logger.info("没有更多数据" ) break for div in quote_divs: text = div.find("span" , class_="text" ).get_text() author = div.find("small" , class_="author" ).get_text() tags = [t.get_text() for t in div.find_all("a" , class_="tag" )] quotes.append(Quote(text=text, author=author, tags=tags)) time.sleep(1 ) return quotes def save_quotes (quotes: list [Quote], filepath: str = "quotes.json" ) -> None : data = [asdict(q) for q in quotes] Path(filepath).write_text( json.dumps(data, ensure_ascii=False , indent=2 ), encoding="utf-8" ) logger.info(f"已保存 {len (quotes)} 条数据到 {filepath} " ) if __name__ == "__main__" : url = "https://quotes.toscrape.com" quotes = scrape_quotes(url, max_pages=3 ) save_quotes(quotes) for q in quotes[:3 ]: print (f"「{q.text} 」 —— {q.author} " )
25.3 数据分析脚本 """CSV 销售数据分析 覆盖:CSV 读取、collections、排序、格式化输出 """ import csvfrom collections import Counter, defaultdictfrom dataclasses import dataclass@dataclass class SaleRecord : product: str category: str price: float quantity: int @property def revenue (self ) -> float : return self .price * self .quantity def analyze_sales (filepath: str ) -> None : records: list [SaleRecord] = [] with open (filepath, "r" , encoding="utf-8" ) as f: reader = csv.DictReader(f) for row in reader: records.append(SaleRecord( product=row["product" ], category=row["category" ], price=float (row["price" ]), quantity=int (row["quantity" ]), )) total_revenue = sum (r.revenue for r in records) total_orders = len (records) avg_order = total_revenue / total_orders if total_orders else 0 by_category: dict [str , float ] = defaultdict(float ) for r in records: by_category[r.category] += r.revenue product_count = Counter(r.product for r in records) print (f"{'=' *50 } " ) print (f" 销售分析报告" ) print (f"{'=' *50 } " ) print (f" 总营收: ¥{total_revenue:>12 ,.2 f} " ) print (f" 总订单: {total_orders:>12 ,} " ) print (f" 平均单价: ¥{avg_order:>12 ,.2 f} " ) print (f"\n {'品类营收' :-^40 } " ) for cat, rev in sorted (by_category.items(), key=lambda x: x[1 ], reverse=True ): pct = rev / total_revenue * 100 bar = "█" * int (pct / 2 ) print (f" {cat:<8 } ¥{rev:>10 ,.2 f} {pct:5.1 f} % {bar} " ) print (f"\n {'热销 Top 5' :-^40 } " ) for product, cnt in product_count.most_common(5 ): print (f" {product:<15 } {cnt} 单" ) if __name__ == "__main__" : analyze_sales("sales.csv" )
25.4 简易 HTTP API 服务器 """简易 REST API 服务器(标准库实现) 覆盖:HTTP 协议、JSON、路由、异常处理 """ import jsonimport loggingfrom http.server import HTTPServer, BaseHTTPRequestHandlerfrom urllib.parse import urlparse, parse_qsfrom dataclasses import dataclass, asdict, fieldlogging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @dataclass class Todo : id : int title: str done: bool = False class TodoStore : def __init__ (self ): self ._todos: dict [int , Todo] = {} self ._next_id = 1 def add (self, title: str ) -> Todo: todo = Todo(id =self ._next_id, title=title) self ._todos[todo.id ] = todo self ._next_id += 1 return todo def get_all (self ) -> list [Todo]: return list (self ._todos.values()) def get (self, todo_id: int ) -> Todo | None : return self ._todos.get(todo_id) def update (self, todo_id: int , **kwargs ) -> Todo | None : todo = self ._todos.get(todo_id) if todo: for k, v in kwargs.items(): setattr (todo, k, v) return todo def delete (self, todo_id: int ) -> bool : return self ._todos.pop(todo_id, None ) is not None store = TodoStore() class TodoHandler (BaseHTTPRequestHandler ): def _json_response (self, data, status=200 ): self .send_response(status) self .send_header("Content-Type" , "application/json; charset=utf-8" ) self .end_headers() self .wfile.write(json.dumps(data, ensure_ascii=False , default=str ).encode()) def _read_body (self ) -> dict : length = int (self .headers.get("Content-Length" , 0 )) return json.loads(self .rfile.read(length)) if length else {} def _send_404 (self ): self ._json_response({"error" : "Not Found" }, 404 ) def do_GET (self ): parsed = urlparse(self .path) if parsed.path == "/todos" : todos = [asdict(t) for t in store.get_all()] self ._json_response(todos) elif parsed.path.startswith("/todos/" ): try : todo_id = int (parsed.path.split("/" )[-1 ]) todo = store.get(todo_id) self ._json_response(asdict(todo) if todo else self ._send_404()) except ValueError: self ._send_404() else : self ._send_404() def do_POST (self ): if self .path == "/todos" : body = self ._read_body() todo = store.add(body.get("title" , "无标题" )) self ._json_response(asdict(todo), 201 ) else : self ._send_404() def do_DELETE (self ): if self .path.startswith("/todos/" ): todo_id = int (self .path.split("/" )[-1 ]) if store.delete(todo_id): self ._json_response({"message" : "已删除" }) else : self ._send_404() def log_message (self, format , *args ): logger.info(f"{self.address_string()} {format % args} " ) if __name__ == "__main__" : server = HTTPServer(("localhost" , 8000 ), TodoHandler) print ("🚀 Server running on http://localhost:8000" ) print (" GET /todos — 获取所有待办" ) print (" POST /todos — 创建待办 (body: {\"title\": \"...\"})" ) print (" DELETE /todos/<id> — 删除待办" ) server.serve_forever()
25.5 密码管理器(加密) """简单密码管理器 —— 学习加密与安全 覆盖:加密、文件 I/O、base64、异常处理 """ import jsonimport base64import secretsimport hashlibfrom pathlib import Pathfrom getpass import getpassfrom cryptography.fernet import Fernetfrom cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMACfrom cryptography.hazmat.primitives import hashesVAULT_FILE = Path("vault.enc" ) SALT_SIZE = 16 def derive_key (master_password: str , salt: bytes ) -> bytes : """从主密码派生加密密钥""" kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32 , salt=salt, iterations=480000 , ) return base64.urlsafe_b64encode(kdf.derive(master_password.encode())) def load_vault (master_password: str ) -> dict : if not VAULT_FILE.exists(): return {} raw = VAULT_FILE.read_bytes() salt = raw[:SALT_SIZE] encrypted = raw[SALT_SIZE:] key = derive_key(master_password, salt) f = Fernet(key) try : decrypted = f.decrypt(encrypted) return json.loads(decrypted) except Exception: raise ValueError("主密码错误或数据已损坏" ) def save_vault (data: dict , master_password: str ) -> None : salt = secrets.token_bytes(SALT_SIZE) key = derive_key(master_password, salt) f = Fernet(key) plaintext = json.dumps(data, ensure_ascii=False ).encode() encrypted = f.encrypt(plaintext) VAULT_FILE.write_bytes(salt + encrypted) def main () -> None : master = getpass("请输入主密码: " ) try : vault = load_vault(master) except ValueError: if VAULT_FILE.exists(): print ("密码错误!" ) return vault = {} print (f"已加载 {len (vault)} 条记录" ) while True : cmd = input ("\n(list/add/get/quit) > " ).strip() if cmd == "quit" : break elif cmd == "list" : for name in vault: print (f" • {name} " ) elif cmd == "add" : name = input (" 服务名: " ) username = input (" 用户名: " ) password = getpass(" 密码: " ) vault[name] = {"username" : username, "password" : password} save_vault(vault, master) print (" 已保存 ✓" ) elif cmd == "get" : name = input (" 服务名: " ) if name in vault: print (f" 用户名: {vault[name]['username' ]} " ) print (f" 密码: {vault[name]['password' ]} " ) else : print (" 未找到" ) if __name__ == "__main__" : main()
附录 A Python 3.10~3.13 新特性速查 Python 3.10(2021) match command: case ["quit" ]: print ("退出" ) case ["go" , direction]: print (f"走向 {direction} " ) def func (x: int | str ) -> bool : ...
Python 3.11(2022) try : raise ExceptionGroup("多个错误" , [ ValueError("错误1" ), TypeError("错误2" ), ]) except * ValueError as eg: print (f"ValueError: {eg.exceptions} " ) except * TypeError as eg: print (f"TypeError: {eg.exceptions} " ) import tomllib
Python 3.12(2023) f"{'hello' :>10 } " def first [T](items: list [T]) -> T: return items[0 ] type Point = tuple [float , float ] def func [*T](*args: *T) -> tuple [*T]: ...x = "outer" result = [x for x in range (5 )] print (x)
Python 3.13(2024)
附录 B 常见陷阱与最佳实践 陷阱 1:可变默认参数 def bad (x, lst=[] ): lst.append(x) return lst def good (x, lst=None ): if lst is None : lst = [] lst.append(x) return lst
陷阱 2:浮点数精度 0.1 + 0.2 == 0.3 import mathmath.isclose(0.1 + 0.2 , 0.3 )
陷阱 3:闭包中的循环变量 funcs = [lambda : i for i in range (5 )] print ([f() for f in funcs]) funcs = [lambda i=i: i for i in range (5 )] print ([f() for f in funcs])
陷阱 4:浅拷贝 vs 深拷贝 import copya = [[1 , 2 ], [3 , 4 ]] b = a.copy() c = copy.deepcopy(a) b[0 ][0 ] = 99 print (a[0 ][0 ]) c[0 ][0 ] = 0 print (a[0 ][0 ])
陷阱 5:字典遍历时修改 d = {"a" : 1 , "b" : 2 , "c" : 3 } to_delete = [k for k, v in d.items() if v < 2 ] for k in to_delete: del d[k]
最佳实践总结
建议
说明
用 is 判断 None
if x is None 比 == None 更精确
用 EAFP
先做再说(try/except),而不是先检查再做
用 pathlib
比 os.path 更面向对象、更优雅
用 f-string
比 % 和 .format() 更可读
用 dataclass
减少样板代码,自动生成方法
用 with 管理资源
确保文件、锁等资源被正确释放
用生成器处理大数据
惰性求值,不占内存
用虚拟环境
隔离项目依赖
写类型注解
提高可读性,配合 mypy 提前发现 bug
用 pytest
比 unittest 更简洁、更强大
Python 完整学习文档 —— 25 个核心主题 · 250+ 代码示例 · 从入门到实战
最后更新: 2024-01 · 适用版本: Python 3.10+ ```