阶段定位 建立 Python 编程的根基,掌握语言核心机制与开发环境配置。此阶段的重点是”知其然且知其所以然”,养成 Pythonic 的编码直觉。
1. 环境搭建与工具链 1.1 Python 解释器 Python 是解释型语言,代码由解释器逐行翻译执行。工作中常见的解释器实现有三种:
解释器
适用场景
特点
CPython
通用开发、生产环境(最主流)
官方标准实现,C 语言编写,兼容性与生态最好
PyPy
计算密集型、长时运行的服务端
自带 JIT 编译器,纯 Python 代码可提速 5-10 倍,但启动慢、C 扩展兼容性有限
Anaconda
数据科学、机器学习
预装 150+ 科学计算包,自带 conda 包管理器
Python 执行流程详解 :理解 Python 代码从编写到执行的完整过程,是深入理解语言机制的基础。
1 2 3 4 5 源代码 (.py) ↓ 编译(Compile) 字节码 (.pyc) ← 可被缓存,下次直接加载 ↓ 解释执行(PVM) 运行结果
编译阶段 :Python 解释器将 .py 源码编译为字节码(Bytecode),字节码是平台无关的中间代码,存储在 __pycache__ 目录下的 .pyc 文件中。如果源码未修改且 .pyc 存在,则跳过编译直接加载字节码。
执行阶段 :Python 虚拟机(PVM, Python Virtual Machine)逐条读取字节码并执行。PVM 是 Python 运行的核心引擎,负责将字节码翻译为机器可执行的指令。
1 2 3 4 5 6 7 8 9 10 import dis dis.dis("x = 1; y = x + 2" )
CPython vs PyPy 性能对比数据 :
基准测试
CPython 3.11
PyPy 7.3 (3.10 兼容)
提速比
纯数值计算(斐波那契)
基准
约 5-8x 快
⬆
字符串处理
基准
约 3-5x 快
⬆
Django 请求处理
基准
约 2-3x 快
⬆
含 C 扩展的库(NumPy)
基准
可能更慢或兼容性问题
⬇/➡
启动时间
快
较慢(JIT 预热)
⬇
Python 版本选择建议 :
版本
发布时间
核心新特性
推荐场景
3.10
2021.10
match/case 模式匹配、结构模式匹配、精确错误定位
稳定生产环境
3.11
2022.10
速度提升 10-60%、ExceptionGroup/except*、Self 类型
当前最推荐的生产版本
3.12
2023.10
更快的推导式、type 参数语法(PEP 695)、灵活的 f-string
新项目、尝鲜
3.13
2024.10
实验性 GIL-free 模式、改进交互解释器、JIT 实验性编译器
前沿探索
1 2 3 4 5 6 python -c "import sys; print(sys.executable); print(sys.version)"
面试真题 :Python 是解释型语言还是编译型语言?请解释 Python 的执行流程。
易错点 :Python 并非纯解释型语言。它先将源码编译成字节码(.pyc),再由 PVM 解释执行字节码。因此 Python 兼具编译和解释的特征。说”Python 是纯解释型语言”是不准确的。
1.2 虚拟环境 虚拟环境是 Python 项目的”隔离舱”,每个项目拥有独立的依赖空间。
虚拟环境工作原理 :虚拟环境通过修改 sys.prefix 和 PATH 环境变量来实现隔离,而非物理复制整个 Python 解释器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 系统 Python (/usr/bin/python3) │ ├── site-packages/ ← 全局第三方包 │ ├── requests/ │ └── numpy/ │ ├── 项目A (.venv/) ← 独立环境 │ ├── pyvenv.cfg ← 指向基础 Python │ ├── lib/ │ │ └── site-packages/ │ │ └── requests==2.28.0 │ └── bin/python → 系统Python(修改了sys.prefix) │ └── 项目B (.venv/) ← 独立环境 ├── pyvenv.cfg └── lib/ └── site-packages/ └── requests==2.31.0
关键机制:
软链接 :虚拟环境中的 Python 可执行文件是指向系统 Python 的软链接(或脚本),但 sys.prefix 被修改为虚拟环境目录。
路径优先 :激活虚拟环境后,PATH 中虚拟环境的 bin/ 目录排在最前,确保优先使用虚拟环境中的 pip 和 python。
隔离原理 :site-packages 目录独立,每个虚拟环境有自己的第三方包目录。
工具
命令示例
适用场景
venv(内置)
python -m venv .venv
最轻量、无额外依赖,日常开发首选
virtualenv
virtualenv -p python3.11 venv
需要指定不同 Python 版本时使用
conda
conda create -n myenv python=3.11
数据科学栈,管理非 Python 依赖(如 CUDA)
pip 配置文件 (加速下载与镜像源配置):
1 2 3 4 5 6 7 8 [global] index-url = https://pypi.tuna.tsinghua.edu.cn/simpletrusted-host = pypi.tuna.tsinghua.edu.cn[install] require-virtualenv = true
pyproject.toml 配置示例 (现代 Python 项目标准):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [project] name = "my-project" version = "0.1.0" description = "A modern Python project" requires-python = ">=3.10" dependencies = [ "requests>=2.28" , "rich>=13.0" , ] [project.optional-dependencies] dev = ["pytest" , "black" , "ruff" ][tool.ruff] line-length = 88 select = ["E" , "F" , "W" ][tool.pytest.ini_options] testpaths = ["tests" ]
最佳实践 :
每个项目根目录创建 .venv 或 venv 目录,并加入 .gitignore。
养成”进入项目先激活环境”的肌肉记忆:1 2 3 4 .\.venv\Scripts\Activate.ps1 source .venv/bin/activate
导出依赖锁定文件,确保团队环境一致:1 2 3 pip freeze > requirements.txt pip install pip-tools pip-compile requirements.in
常见环境问题排查 :
问题
原因
解决方案
ModuleNotFoundError
未安装包 / 未激活虚拟环境
pip install <pkg> 或激活 .venv
pip install 安装到全局
未激活虚拟环境
先激活虚拟环境,或配置 require-virtualenv = true
权限被拒(PowerShell)
执行策略限制
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
python 命令找不到
Windows 未添加 PATH
安装时勾选 “Add Python to PATH”,或手动添加
版本冲突
多个 Python 版本混用
使用 py -3.11(Windows launcher)或 python3.11 指定版本
面试真题 :虚拟环境是如何实现隔离的?venv 和 virtualenv 有什么区别?
易错点 :虚拟环境不是完整复制 Python 解释器,而是通过修改 sys.prefix 和 PATH 实现隔离。venv 是 Python 3.3+ 内置模块,功能精简;virtualenv 是第三方包,支持更多特性(如指定不同 Python 版本、更快创建速度)。
1.3 IDE / 编辑器选择
工具
定位
核心优势
PyCharm
专业级 IDE
代码分析、重构、调试、数据库工具集成最完善
VS Code
轻量编辑器 + 插件
启动快、插件丰富、多语言通用
Jupyter
交互式探索
数据分析、算法验证、教学演示的神器
各 IDE 推荐插件列表 :
PyCharm 插件 :
.ignore:生成各类 .gitignore 模板
Key Promoter X:快捷键提示,帮助脱离鼠标
Rainbow Brackets:彩色括号匹配
String Manipulation:字符串格式转换(驼峰/下划线/常量)
GitToolBox:增强 Git 集成
.env files support:环境变量文件高亮
VS Code 插件 :
Python(Microsoft):核心 Python 支持(IntelliSense、调试)
Pylance:高性能类型检查和自动补全
Ruff:极速 linter + formatter(替代 flake8 + isort + black)
Python Test Explorer:测试可视化运行
Python Docstring Generator:自动生成 docstring
Jupyter:笔记本支持
Error Lens:行内显示错误信息
调试器配置技巧 :
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 { "version" : "0.2.0" , "configurations" : [ { "name" : "Python: Current File" , "type" : "python" , "request" : "launch" , "program" : "${file}" , "console" : "integratedTerminal" , "justMyCode" : true } , { "name" : "Python: Flask" , "type" : "python" , "request" : "launch" , "module" : "flask" , "env" : { "FLASK_APP" : "app.py" , "FLASK_DEBUG" : "1" } , "args" : [ "run" , "--port=5000" ] } ] }
调试高效技巧 :
条件断点 :右键断点 → 设置条件(如 i == 100),只在满足条件时暂停
日志断点 :不暂停执行,仅打印日志(右键断点 → 选择 “Logpoint”)
调试时表达式求值 :在 Debug Console 中可实时执行表达式
远程调试 :使用 debugpy 库进行远程调试
1 2 3 4 5 def compute (data ): result = process(data) breakpoint () return result
老工程师的选择逻辑 :
大型项目、Web 后端开发 → PyCharm Professional
全栈/多语言开发、远程开发 → VS Code + Python 插件
数据探索、模型实验 → Jupyter Lab
面试真题 :你在项目中如何选择开发工具?如何提高调试效率?
易错点 :初学者常忽视 breakpoint() 的使用,仍然使用 print() 调试。在生产代码中应删除断点语句,或通过 PYTHONBREAKPOINT=0 环境变量禁用。
1.4 包管理
工具
设计哲学
核心文件
推荐度
pip
基础安装器
requirements.txt
必会基础
poetry
现代全功能
pyproject.toml + poetry.lock
⭐ 新项目首选
pipenv
虚拟环境+包管理一体
Pipfile + Pipfile.lock
社区活跃度下降,新项目不推荐
pip 常用命令速查表 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pip install requests pip install requests==2.28.0 pip install "requests>=2.28,<3.0" pip install -r requirements.txt pip install -e . pip list pip show requests pip outdated pip uninstall requests pip freeze > requirements.txt pip check pip cache purge pip autoremove
requirements.txt vs pyproject.toml 对比 :
特性
requirements.txt
pyproject.toml
标准化
非官方约定
PEP 517/518 官方标准
依赖分组
不支持(需多文件)
原生支持(optional-dependencies)
元数据
不支持
项目名称、版本、作者等完整元数据
构建系统
不涉及
可定义构建后端(setuptools/flit/poetry)
锁定版本
需手动 pip freeze
poetry.lock / 精确依赖解析
生态兼容
pip 通用
需要构建工具(poetry/build)
推荐场景
简单脚本、Docker 部署
正式项目、可发布包
Poetry 快速上手 (2024 年最推荐的方案):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pip install poetry poetry new my-project cd my-projectpoetry add requests poetry add pytest --group dev poetry install poetry run python main.py poetry shell
Poetry 项目结构 :
1 2 3 4 5 6 7 8 9 10 11 my-project/ ├── pyproject.toml ← 项目配置 + 依赖声明 ├── poetry.lock ← 锁定的精确依赖版本(提交到 Git) ├── README.md ├── src/ │ └── my_project/ │ ├── __init__.py │ └── main.py └── tests/ ├── __init__.py └── test_main.py
面试真题 :requirements.txt 和 pyproject.toml 有什么区别?如何管理项目的依赖?
易错点 :pip freeze 会导出所有包(包括间接依赖),导致 requirements.txt 臃肿。推荐使用 pip-compile(pip-tools)或 Poetry,只声明直接依赖,由工具自动解析间接依赖。
2. 基本语法 2.1 变量、标识符、关键字 变量本质 :Python 中的变量是指向对象的”标签”(引用),而非存储数据的容器。这是理解 Python 内存模型的核心概念。
1 2 3 4 a = [1 , 2 , 3 ] b = a b.append(4 ) print (a)
Python 变量内存模型图解 :
1 2 3 4 5 6 7 变量名(标签) 内存中的对象 ┌──────┐ ┌──────────────┐ │ a │────────→│ list [1,2,3] │ type: list └──────┘ │ id: 140xxx │ value: [1,2,3,4] ┌──────┐ │ refcnt: 2 │ ← 引用计数为2(a和b都指向它) │ b │────────→│ │ └──────┘ └──────────────┘
Python 的变量赋值本质是”让一个名字指向一个对象”,而非”把数据装进盒子”。因此,多个变量可以指向同一个对象。
id() 函数和 is 运算符的关系 :
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 a = [1 , 2 , 3 ] b = a c = [1 , 2 , 3 ] print (id (a)) print (id (b)) print (id (c)) print (a is b) print (a is c) print (a == c) a = 256 b = 256 print (a is b) c = 257 d = 257 print (c is d) x = None if x is None : pass if x == None : pass
小整数池机制(-5 ~ 256) :
CPython 在启动时预创建 -5 到 256 的整数对象,这些对象在整个解释器生命周期内被缓存复用。因此,任何指向这些值的变量都会指向同一个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 a = -5 b = -5 print (a is b) a = 256 b = 256 print (a is b) a = 257 b = 257 print (a is b) x = 257 ; y = 257 print (x is y)
字符串驻留机制 :
Python 会将符合特定规则的字符串进行”驻留”(intern),使相同内容的字符串共享同一个对象,节省内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 a = "hello" b = "hello" print (a is b) a = "hello!" b = "hello!" print (a is b) import sysa = sys.intern("hello!" ) b = sys.intern("hello!" ) print (a is b)
命名规范(PEP 8) :
snake_case:变量、函数名(如 user_name、calculate_total)
PascalCase:类名(如 UserService、HttpResponse)
UPPER_CASE:常量(如 MAX_RETRY_COUNT、DEFAULT_TIMEOUT)
_single_leading_underscore:”内部使用”约定,不公开 API
__double_leading_underscore:触发名称改写(name mangling),用于避免子类属性冲突
命名反面教材 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 l = [1 , 2 , 3 ] O = 100 I = 200 data = [1 , 2 , 3 ] flag = True tmp = compute() d1 = get_data() user_list = [1 , 2 , 3 ] max_retries = 100 payment_processed = True computed_value = compute() primary_data = get_data()
关键字速查 (不可作为标识名):
1 2 3 4 5 6 7 8 9 10 11 import keywordprint (keyword.kwlist)print (keyword.softkwlist)
面试真题 :Python 中 is 和 == 的区别是什么?为什么 a = 257; b = 257; a is b 在交互模式和脚本中结果可能不同?
易错点 :① 误用 is 比较值相等性(应使用 ==);② 误以为 is 比较的是值而非对象身份;③ 不知道小整数池的范围是 -5 到 256;④ 在 for 循环中用 is 判断是否等于某个整数,结果可能不符合预期。
2.2 缩进与代码块 Python 使用缩进 定义代码块,这是语法的一部分,不是风格建议。
1 2 3 4 5 6 7 def check_age (age ): if age >= 18 : print ("成年人" ) if age >= 60 : print ("老年人" ) else : print ("未成年人" )
缩进错误排查技巧 :
TabError: inconsistent use of tabs and spaces in indentation
1 2 python -c "with open('file.py') as f: [print(f'Line {i+1}: Tab found') for i, line in enumerate(f) if '\t' in line]"
IndentationError: unindent does not match any outer indentation level
最常见原因:混用空格和 Tab
复制粘贴代码后未统一缩进
不同编辑器 Tab 宽度设置不一致
自动修复工具 :
1 2 3 4 5 6 7 8 9 10 11 12 pip install autopep8 autopep8 --in-place --aggressive file.py pip install black black file.py pip install ruff ruff format file.py ruff check --fix file.py
血泪教训 :
永远不要混用空格和 Tab,团队统一使用 4 个空格 。
在 .editorconfig 中配置,从源头杜绝:1 2 3 [*.py] indent_style = spaceindent_size = 4
复制粘贴代码时,若出现 IndentationError,优先检查是否混入了 Tab。
在 Git 中设置 git config --global core.autocrlf false,避免换行符差异导致缩进问题。
面试真题 :Python 为什么用缩进而非花括号?缩进混用空格和 Tab 会怎样?
易错点 :Tab 和空格在视觉上可能一样,但 Python 解释器会严格区分。PEP 8 规定使用 4 个空格,Tab 只在已有代码中与 Tab 保持一致时使用。
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 x = x + 1 """ 这是模块级别的说明,通常放在文件顶部。 说明模块职责、主要类和函数的概要。 """ def calculate_area (radius ): """返回圆的面积。 Args: radius (float): 圆的半径,必须大于 0。 Returns: float: 圆的面积。 Raises: ValueError: 如果 radius <= 0。 Example: >>> calculate_area(5) 78.53981633974483 """ if radius <= 0 : raise ValueError("半径必须大于 0" ) import math return math.pi * radius ** 2
Sphinx 文档生成流程 :
1 2 3 4 5 docstring(Google/NumPy 风格) ↓ sphinx-apidoc / autodoc 扩展 .rst 文件(reStructuredText) ↓ sphinx-build HTML 文档网站
1 2 3 4 5 6 7 8 9 10 11 12 pip install sphinx sphinx-rtd-theme cd docssphinx-quickstart sphinx-apidoc -o source ../src/my_package sphinx-build -b html source build
type hint 配合 docstring 的最佳实践 :
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 from typing import Optional , Union def search_user ( user_id: int , *, active_only: bool = True , fields: Optional [list [str ]] = None , ) -> Optional [dict [str , Union [str , int ]]]: """根据 ID 搜索用户。 Args: user_id: 用户唯一标识符,必须为正整数。 active_only: 是否只搜索活跃用户,默认 True。 fields: 需要返回的字段列表,None 表示全部字段。 Returns: 用户信息字典,未找到时返回 None。 Raises: ValueError: user_id 不是正整数时。 Example: >>> search_user(42, active_only=False) {'id': 42, 'name': 'Alice', 'status': 'inactive'} """ if user_id <= 0 : raise ValueError("user_id 必须为正整数" )
工程规范 :
公开 API(函数、类、模块)必须有文档字符串(docstring)。
推荐使用 Google 风格 或 NumPy 风格 ,配合 Sphinx 自动生成文档。
私有函数(_ 开头)可简化注释,但复杂逻辑仍需说明。
type hint 和 docstring 互补:type hint 提供机器可读的类型信息,docstring 提供语义说明。
面试真题 :Python 的 docstring 有哪几种风格?如何在项目中自动生成 API 文档?
易错点 :混淆注释和文档字符串的用途。注释是给开发者看的,docstring 是 API 文档的一部分,会被 help() 和 Sphinx 提取。单行注释用 #,不要用三引号代替。
3. 数据类型与数据结构 Python 中一切皆为对象,每个对象都有类型 、值 和身份(id) 。
3.1 数字类型 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 big = 2 ** 1000 print (type (big)) a = 0.1 + 0.2 print (a) print (a == 0.3 ) import mathprint (math.isclose(a, 0.3 )) from decimal import Decimal, getcontextgetcontext().prec = 6 print (Decimal('0.1' ) + Decimal('0.2' )) z = 3 + 4j print (abs (z)) print (True + True ) print (isinstance (True , int ))
整数缓存机制详解 :
CPython 不仅缓存 -5 到 256 的小整数,还会对某些特定值做优化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import sysprint (sys.getsizeof(0 )) print (sys.getsizeof(1 )) print (sys.getsizeof(2 **30 )) print (sys.getsizeof(2 **60 )) print (sys.getsizeof(2 **1000 )) print (True == 1 ) print (False == 0 ) print (True is 1 )
浮点数 IEEE 754 详解 :
1 2 3 4 5 6 7 8 9 10 11 IEEE 754 双精度浮点数(64 位)结构: ┌─────┬──────────────┬─────────────────────────────────────┐ │ 符号 │ 指数(11位) │ 尾数(52位) │ │ 1位 │ 范围: -1022 │ 精度: 约 15-17 位有效数字 │ │ │ ~ +1023 │ │ └─────┴──────────────┴─────────────────────────────────────┘ 特殊值: - inf / -inf:无穷大(1.0 / 0.0) - nan:非数字(0.0 / 0.0 或 float('nan')) - -0.0:负零(-0.0 == 0.0 为 True,但 1/0.0 报错而 1/-0.0 为 -inf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 print (f"{0.1 :.60 f} " ) print (f"{0.2 :.60 f} " ) print (f"{0.3 :.60 f} " ) print (float ('inf' )) print (float ('-inf' )) print (float ('nan' )) print (1.0 / 0.0 ) import mathprint (math.inf) print (math.nan) print (math.isnan(math.nan)) print (math.nan == math.nan)
Decimal 精度控制实战 :
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 from decimal import Decimal, getcontext, ROUND_HALF_UP, ROUND_DOWNgetcontext().prec = 28 price = Decimal('19.99' ) tax_rate = Decimal('0.08' ) tax = (price * tax_rate).quantize(Decimal('0.01' ), rounding=ROUND_HALF_UP) print (tax) value = Decimal('2.675' ) print (value.quantize(Decimal('0.01' ), rounding=ROUND_HALF_UP)) print (value.quantize(Decimal('0.01' ), rounding=ROUND_DOWN)) print (Decimal(0.1 )) print (Decimal('0.1' )) with getcontext() as ctx: ctx.prec = 6 result = Decimal('1' ) / Decimal('3' ) print (result)
Fraction 分数类型 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from fractions import Fractiona = Fraction(1 , 3 ) b = Fraction(2 , 3 ) print (a + b) print (Fraction(0.1 )) print (Fraction('0.1' )) print (Fraction(6 , 4 )) print (float (Fraction(1 , 3 )))
面试真题 :为什么 0.1 + 0.2 != 0.3?如何在 Python 中进行精确的小数运算?
易错点 :① Decimal(0.1) 仍然会有精度问题,必须用 Decimal('0.1');② math.nan == math.nan 返回 False,判断 nan 必须用 math.isnan();③ 布尔值参与算术运算虽然合法,但代码可读性极差。
3.2 序列类型 字符串 str(不可变) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 s = "Hello, Python!" print (s[0 ]) print (s[-1 ]) print (s[7 :13 ]) print (s[::2 ]) print (s[::-1 ]) name = "Alice" age = 30 print (f"姓名: {name} , 年龄: {age} , 明年: {age + 1 } " )query = """ SELECT id, name FROM users WHERE age > %s """ path = r"C:\Users\name\file.txt"
字符串切片可视化 :
1 2 3 4 5 6 7 8 9 10 11 12 字符串: H e l l o , P y t h o n ! 正索引: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 负索引:-14-13-12-11-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 s[7:13] → ┌───────────────┐ │ P y t h o n │ └───────────────┘ s[::2] → H . l . o . . P . t . o . ! ↑ ↑ ↑ ↑ ↑ ↑ ↑ s[::-1] → ! n o h t y P , o l l e H (反转)
切片通用公式 :s[start:stop:step]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 s = "Hello, Python!" print (s[3 :8 ]) print (s[-6 :]) print (s[:5 ]) print (s[5 :]) print (s[::-1 ]) print (s[::-2 ]) print (s[100 :]) print (s[:100 ])
字符串编码与解码详解 :
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 text = "你好,世界" b = text.encode("utf-8" ) print (len (b)) b_gbk = text.encode("gbk" ) print (len (b_gbk)) decoded = b.decode("utf-8" ) bad_bytes = b'\xff\xfe' print (bad_bytes.decode("utf-8" , errors="ignore" )) print (bad_bytes.decode("utf-8" , errors="replace" )) print (bad_bytes.decode("utf-8" , errors="backslashreplace" ))
面试真题 :Python 中字符串的切片和索引有什么区别?切片越界会怎样?字符串编码和解码的过程是什么?
易错点 :① 切片越界不报错但索引越界报 IndexError;② s[::-1] 不是原地修改,而是返回新字符串;③ 编码和解码方向搞反——str.encode() 得到 bytes,bytes.decode() 得到 str。
bytes 与 bytearray(二进制序列)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 b = b"hello" b = bytes ([104 , 101 , 108 , 108 , 111 ]) b = "hello" .encode("utf-8" ) print (b[0 ]) ba = bytearray (b"hello" ) ba[0 ] = 104 ba.append(33 ) print (ba)
列表 list(可变,有序) 1 2 3 4 5 6 7 8 9 10 11 12 squares = [x**2 for x in range (10 )] evens = [x for x in range (10 ) if x % 2 == 0 ] labels = ["even" if x % 2 == 0 else "odd" for x in range (5 )] matrix = [[1 , 2 , 3 ], [4 , 5 , 6 ], [7 , 8 , 9 ]] flat = [x for row in matrix for x in row]
列表 28 种常用方法速查 :
类别
方法
说明
时间复杂度
添加
append(x)
尾部追加
O(1)均摊
extend(iter)
追加可迭代对象
O(k)
insert(i, x)
指定位置插入
O(n)
删除
remove(x)
删除第一个值为 x 的元素
O(n)
pop()
弹出最后一个元素
O(1)
pop(i)
弹出指定位置元素
O(n)
clear()
清空列表
O(1)
查找
index(x)
返回 x 的索引
O(n)
count(x)
统计 x 出现次数
O(n)
x in lst
成员检测
O(n)
排序
sort()
原地排序
O(n log n)
reverse()
原地反转
O(n)
复制
copy()
浅拷贝
O(n)
其他
len(lst)
长度
O(1)
lst[i]
索引访问
O(1)
lst[i] = x
索引赋值
O(1)
lst + lst2
拼接(返回新列表)
O(n+m)
lst * n
重复(注意嵌套陷阱!)
O(n*k)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 a = [[0 ]] * 3 a[0 ][0 ] = 1 a = [[0 ] for _ in range (3 )] a[0 ][0 ] = 1 data = [("Alice" , 30 ), ("Bob" , 25 ), ("Charlie" , 35 )] data.sort(key=lambda x: x[1 ]) data.sort(key=lambda x: x[1 ], reverse=True ) employees = [ {"name" : "Alice" , "dept" : "Engineering" , "salary" : 120000 }, {"name" : "Bob" , "dept" : "Engineering" , "salary" : 110000 }, {"name" : "Charlie" , "dept" : "Sales" , "salary" : 90000 }, ] employees.sort(key=lambda x: x["salary" ]) employees.sort(key=lambda x: x["dept" ])
常见操作时间复杂度(面试常考) :
索引访问 lst[i]:O(1)
尾部追加 append():均摊 O(1)
任意位置插入 insert(i, x):O(n)
查找 x in lst:O(n)
排序 sort():O(n log n)
深浅拷贝(面试高频) :
1 2 3 4 5 6 7 8 import copyoriginal = [[1 , 2 ], [3 , 4 ]] shallow = original.copy() deep = copy.deepcopy(original) original[0 ][0 ] = 99 print (shallow) print (deep)
深浅拷贝内存模型对比 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 浅拷贝 (shallow copy): original ──→ [ ┌──┐ , ┌──┐ ] │ │ │ │ shallow ──→ [ └│┘ └│┘ ] ← 外层列表是新的,但内层对象共享! │ │ ↓ ↓ [99,2] [3,4] ← 修改 original[0][0] 会影响 shallow 深拷贝 (deep copy): original ──→ [ ┌──┐ , ┌──┐ ] ↓ ↓ [99,2] [3,4] ← 原始对象 deep ──→ [ ┌──┐ , ┌──┐ ] ← 完全独立的新列表 ↓ ↓ [1,2] [3,4] ← 内层对象也被递归复制,互不影响
三种浅拷贝方式的区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 lst = [1 , [2 , 3 ], 4 ] c1 = lst.copy() c2 = list (lst) c3 = lst[:]
面试真题 :Python 中深拷贝和浅拷贝有什么区别?list.copy() 是深拷贝还是浅拷贝?如何实现一个真正的深拷贝?
易错点 :① list.copy() / list() / lst[:] 都是浅拷贝;② [[0]] * 3 三个子列表是同一个对象;③ copy.deepcopy() 会递归复制所有嵌套对象,但无法拷贝自定义对象的内部状态(如文件句柄、数据库连接)。
元组 tuple(不可变,有序) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 t = (1 , [2 , 3 ], 4 ) t[1 ].append(4 ) coord = (120.5 , 30.2 ) lng, lat = coord first, *middle, last = [1 , 2 , 3 , 4 , 5 ] print (middle) single = (42 ,) not_tuple = (42 )
元组作为 dict 键的优势 :
1 2 3 4 5 6 7 8 9 10 11 12 location_data = { (35.6762 , 139.6503 ): "东京" , (39.9042 , 116.4074 ): "北京" , (40.7128 , -74.0060 ): "纽约" , }
namedtuple 用法 :
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 from collections import namedtuplePoint = namedtuple('Point' , ['x' , 'y' ]) p = Point(x=3 , y=4 ) print (p.x, p.y) print (p[0 ], p[1 ]) x, y = p coords = [1 , 2 , 3 , 4 ] Point = namedtuple('Point' , ['x' , 'y' , 'z' , 'w' ]) p = Point._make(coords) print (p._asdict()) p2 = p._replace(x=10 ) print (p2) from typing import NamedTupleclass Employee (NamedTuple ): name: str age: int department: str = "Engineering" emp = Employee("Alice" , 30 ) print (emp.name, emp.department)
range 深入理解range 是 Python 中常被低估的序列类型,它不只是 for 循环的辅助工具。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 r = range (0 , 10 , 2 ) print (type (r)) print (list (r)) print (len (r)) print (r[3 ]) print (r.index(4 )) print (r.count(2 )) import sysprint (sys.getsizeof(range (1000000 ))) print (sys.getsizeof(list (range (1000000 )))) print (500000 in range (1000000 ))
3.3 哈希类数据结构 字典 dict(3.7+ 有序,键必须可哈希) 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 word_lengths = {word: len (word) for word in ["apple" , "banana" , "cherry" ]} counts = {"a" : 1 } print (counts.get("b" , 0 )) if "b" not in counts: counts["b" ] = [] counts["b" ].append(2 ) counts.setdefault("c" , []).append(3 ) from collections import defaultdictd = defaultdict(list ) d["d" ].append(4 ) d1 = {"a" : 1 , "b" : 2 } d2 = {"b" : 3 , "c" : 4 } merged = d1 | d2
dict 底层哈希表实现原理 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 哈希表(Hash Table)内部结构: ┌─────────────────────────────────────────────┐ │ 哈希函数 hash(key) → 索引位置 │ │ │ │ Bucket Array(桶数组): │ │ ┌───┬──────────────────┬──────────────┐ │ │ │ 0 │ (空) │ │ │ │ ├───┼──────────────────┼──────────────┤ │ │ │ 1 │ hash=... key='name' val='Alice'│ │ │ ├───┼──────────────────┼──────────────┤ │ │ │ 2 │ (空) │ │ │ │ ├───┼──────────────────┼──────────────┤ │ │ │ 3 │ hash=... key='age' val=30 │ │ │ ├───┼──────────────────┼──────────────┤ │ │ │ 4 │ (碰撞→指向下一位置) │ │ │ └───┴──────────────────┴──────────────┘ │ │ │ │ 查找流程: │ │ 1. hash(key) → 计算哈希值 │ │ 2. hash % len(table) → 定位桶索引 │ │ 3. 比较键是否相等 → 找到或继续探查 │ │ 4. 负载因子 > 2/3 时扩容(重新哈希) │ └─────────────────────────────────────────────┘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 print (hash ("name" )) print (hash (42 )) print (hash ((1 , 2 ))) class Person : def __init__ (self, name ): self .name = name def __eq__ (self, other ): return self .name == other.name def __hash__ (self ): return hash (self .name)
dict vs list 性能对比数据 :
操作
dict 时间复杂度
list 时间复杂度
倍率
查找 x in obj
O(1)
O(n)
dict 快约 100-10000 倍
插入 obj[k] = v
O(1)均摊
append O(1), insert(i,v) O(n)
dict 更稳定
删除 del obj[k]
O(1)
O(n)
dict 快得多
遍历
O(n)
O(n)
相同
内存占用
较大(哈希表开销)
较小
list 更省内存
1 2 3 4 5 6 7 8 9 10 11 12 13 import timeitsetup_dict = "d = {i: i*2 for i in range(100000)}" setup_list = "l = list(range(100000))" dict_time = timeit.timeit("50000 in d" , setup=setup_dict, number=10000 ) list_time = timeit.timeit("50000 in l" , setup=setup_list, number=10000 ) print (f"dict 查找: {dict_time:.4 f} s" ) print (f"list 查找: {list_time:.4 f} s" )
高级字典类型 :
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 from collections import OrderedDictod = OrderedDict() od['z' ] = 1 od['a' ] = 2 od['m' ] = 3 print (od) print (od.popitem(last=True )) print (od.popitem(last=False ))od['a' ] = 2 od.move_to_end('a' ) from collections import ChainMapdefaults = {"theme" : "dark" , "lang" : "en" , "debug" : False } user_config = {"theme" : "light" , "lang" : "zh" } env_config = {"debug" : True } config = ChainMap(env_config, user_config, defaults) print (config["theme" ]) print (config["debug" ]) print (config["lang" ]) from collections import Counterwords = ["apple" , "banana" , "apple" , "cherry" , "banana" , "apple" ] word_counts = Counter(words) print (word_counts) print (word_counts.most_common(2 )) c1 = Counter("aabbc" ) c2 = Counter("abccc" ) print (c1 + c2) print (c1 - c2) print (c1 & c2) print (c1 | c2)
面试真题 :Python 字典的底层实现原理是什么?为什么字典的键必须是可哈希的?dict 和 list 在查找性能上差多少?
易错点 :① Python 3.7+ 的 dict 是有序的(按插入顺序),但不要将 dict 当作有序数据结构使用,OrderedDict 才有 move_to_end、popitem(last=False) 等有序操作;② 自定义类定义了 __eq__ 后不定义 __hash__ 会使实例变为不可哈希。
集合 set(可变)与 frozenset(不可变) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 a = {1 , 2 , 3 , 3 , 3 } b = {3 , 4 , 5 } print (a | b) print (a & b) print (a - b) print (a ^ b) users_seen = set () for user_id in stream: if user_id in users_seen: continue users_seen.add(user_id) process(user_id) fs = frozenset ([1 , 2 , 3 ]) d = {fs: "hello" }
3.4 特殊类型 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 result = None print (result is None ) def append_item (item, lst=[] ): lst.append(item) return lst print (append_item(1 )) print (append_item(2 )) def append_item_safe (item, lst=None ): if lst is None : lst = [] lst.append(item) return lst from typing import Tuple def process (items: Tuple [int , ...] ) -> None : ... data = bytearray (b"hello world" ) mv = memoryview (data) print (mv[0 ]) mv[0 ] = 72 print (data) slice_view = mv[6 :11 ] print (slice_view.tobytes())
Enum 枚举类型 :
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 from enum import Enum, auto, IntEnum, Flag, IntFlagclass Color (Enum ): RED = 1 GREEN = 2 BLUE = 3 print (Color.RED) print (Color.RED.value) print (Color(1 )) print (Color['RED' ]) class Status (Enum ): PENDING = auto() PROCESSING = auto() DONE = auto() class Priority (IntEnum ): LOW = 1 MEDIUM = 2 HIGH = 3 print (Priority.HIGH > Priority.LOW) print (Priority.HIGH == 3 ) class Permission (Flag ): READ = auto() WRITE = auto() EXECUTE = auto() perm = Permission.READ | Permission.WRITE print (perm) print (Permission.READ in perm)
dataclass 数据类 (Python 3.7+):
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 from dataclasses import dataclass, field, asdict, astuple@dataclass class User : name: str age: int email: str = "" tags: list [str ] = field(default_factory=list ) id : int = field(init=False , repr =False ) def __post_init__ (self ): """初始化后自动调用""" self .id = hash (self .name) user = User("Alice" , 30 , tags=["admin" ]) print (user) print (asdict(user)) print (astuple(user)) @dataclass(frozen=True ) class Point : x: float y: float p = Point(1.0 , 2.0 ) print (hash (p))
typing 常用类型提示 :
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 from typing import ( Optional , Union , Literal , TypeAlias, Callable , Iterable, Sequence , Mapping, TypeVar, Generic , Protocol, Any , Never, ) def find_user (user_id: int ) -> Optional [dict ]: ... def process (value: Union [str , int , float ] ) -> str : ... def process (value: str | int | float ) -> str : ... def set_mode (mode: Literal ["train" , "eval" , "test" ] ) -> None : ... Vector: TypeAlias = list [float ] def apply (func: Callable [[int , int ], int ], a: int , b: int ) -> int : return func(a, b) T = TypeVar('T' ) def first (items: list [T] ) -> T: return items[0 ] class Drawable (Protocol ): def draw (self ) -> None : ... def render (obj: Drawable ) -> None : obj.draw()
面试真题 :Python 的 @dataclass 和普通 class 有什么区别?Enum 和普通常量相比有什么优势?
易错点 :① dataclass 中可变默认值必须使用 field(default_factory=list),不能直接写 tags: list = [];② Enum 成员是单例,比较用 is 而非 ==(虽然 == 也能用);③ Optional[T] 不等于 T | None(Python 3.10 以下),但语义相同。
4. 运算符与表达式 4.1 运算符分类
类别
运算符
要点
算术
+ - * / // % **
// 是向下取整除法,-3 // 2 == -2
比较
== != > < >= <=
可链式比较:1 < x < 10
赋值
= += -= *= /=
没有 ++ / --
逻辑
and or not
短路求值,返回最后一个求值的操作数
位运算
& | ^ ~ << >>
直接操作二进制位,效率优化场景使用
成员
in not in
字典判断键:"key" in dict_obj
身份
is is not
判断同一对象,不要用于值比较
整除运算的数学原理 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 print (7 // 2 ) print (-7 // 2 ) print (divmod (7 , 2 )) print (divmod (-7 , 2 )) print (-7 % 2 ) print (7 % -2 )
位运算实战技巧 :
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 READ = 0b100 WRITE = 0b010 EXECUTE = 0b001 user_perm = READ | WRITE admin_perm = READ | WRITE | EXECUTE print (bool (user_perm & READ)) print (bool (user_perm & EXECUTE)) user_perm |= EXECUTE user_perm &= ~WRITE def rgb_to_hex (r, g, b ): return (r << 16 ) | (g << 8 ) | b def hex_to_rgb (hex_color ): r = (hex_color >> 16 ) & 0xFF g = (hex_color >> 8 ) & 0xFF b = hex_color & 0xFF return r, g, b print (hex (rgb_to_hex(255 , 128 , 0 ))) print (hex_to_rgb(0xff8000 )) print (5 << 3 ) print (40 >> 3 ) a, b = 10 , 20 a ^= b; b ^= a; a ^= b print (a, b)
面试真题 :Python 中 -7 // 2 的结果是什么?和 C 语言的结果一样吗?位运算如何实现权限控制?
易错点 :① Python 的 // 是向下取整,负数结果与 C/Java 不同;② % 取模的结果符号与除数相同,不是与被除数相同;③ ~x = -(x+1),而不是简单的按位取反加 1(那是补码视角)。
4.2 逻辑运算符的短路特性 1 2 3 4 5 6 7 8 9 10 11 12 13 result = 0 and 999 result = 5 and 999 result = 5 or 999 result = 0 or 999 greeting = user_input or "Hello" status = "adult" if age >= 18 else "minor"
短路求值的更多实战案例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 result = obj and obj.method() debug and print ("调试信息" ) config = env_value or default_value or "fallback" item = len (lst) > 0 and lst[0 ] or None item = lst[0 ] if lst else None user = db.get_user(id ) or db.create_guest_user()
and/or 实现三元运算 :
1 2 3 4 5 6 7 8 9 10 result = value_if_true if condition else value_if_false result = condition and value_if_true or value_if_false print (True and 0 or "fallback" ) result = 0 if True else "fallback"
面试真题 :Python 中 and 和 or 的返回值是什么?如何利用短路求值实现默认值?
易错点 :① and 和 or 返回的不是 True/False,而是最后一个求值的操作数;② condition and value or fallback 在 value 为假值时会出错,不要用这种方式替代三元运算符;③ not 返回的是 True/False,与 and/or 行为不同。
4.3 海象运算符 :=(3.8+) 在表达式内部进行赋值,减少重复计算:
1 2 3 4 5 6 7 8 9 10 11 12 match = pattern.search(data)if match : print (match .group(1 )) if match := pattern.search(data): print (match .group(1 )) while (line := file.readline().strip()): process(line)
海象运算符的更多使用场景 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 results = [expensive_computation(x) for x in data if expensive_computation(x) > threshold] results = [y for x in data if (y := expensive_computation(x)) > threshold] while (chunk := response.read(8192 )): process(chunk) if (n := len (data)) > 10 : print (f"数据量过大: {n} 条记录" ) discount = 0.1 if (total := sum (prices)) > 1000 else 0 print (f"总价: {total} , 折扣: {discount} " )if any ((invalid := not is_valid(item)) for item in items): print (f"发现无效项: {invalid} " )
海象运算符的限制 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (x := 10 )
面试真题 :海象运算符 := 有什么用?在列表推导式中如何避免重复计算?
易错点 :① 海象运算符不能用于属性赋值(obj.attr := val 是语法错误);② 过度使用会降低代码可读性;③ 在 any()/all() 中使用时,捕获的值是生成器中最后一个被计算的值,可能不是你期望的。
4.4 运算符优先级与运算符重载 运算符优先级(常用优先级,从高到低) :
优先级
运算符
最高
()(括号)
高
**
中
* / // % @
中
+ -
中
== != > < >= <=
低
not
低
and
最低
or
工程建议 :不要依赖记忆优先级,复杂表达式用括号明确意图。
运算符重载(魔术方法)简介 :
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 class Vector : """二维向量,演示运算符重载""" def __init__ (self, x, y ): self .x = x self .y = 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 ): if isinstance (scalar, (int , float )): return Vector(self .x * scalar, self .y * scalar) return NotImplemented def __eq__ (self, other ): if not isinstance (other, Vector): return NotImplemented return self .x == other.x and self .y == other.y def __lt__ (self, other ): return (self .x**2 + self .y**2 ) < (other.x**2 + other.y**2 ) def __repr__ (self ): return f"Vector({self.x} , {self.y} )" def __abs__ (self ): return (self .x**2 + self .y**2 ) ** 0.5 def __bool__ (self ): return self .x != 0 or self .y != 0 v1 = Vector(3 , 4 ) v2 = Vector(1 , 2 ) print (v1 + v2) print (v1 * 3 ) print (v1 == v2) print (v1 > v2) print (abs (v1)) print (bool (v1))
常用运算符与对应魔术方法速查 :
运算符
魔术方法
说明
+
__add__, __radd__
加法 / 右侧加法
-
__sub__, __rsub__
减法
*
__mul__, __rmul__
乘法
//
__floordiv__
整除
**
__pow__
幂运算
==
__eq__
等于
<
__lt__
小于
<=
__le__
小于等于
in
__contains__
成员检测
len()
__len__
长度
str()
__str__
字符串表示
repr()
__repr__
开发者表示
bool()
__bool__
布尔转换
[]
__getitem__, __setitem__
索引访问/赋值
()
__call__
可调用对象
面试真题 :Python 如何实现运算符重载?__eq__ 和 __hash__ 的关系是什么?
易错点 :① 运算符重载方法返回 NotImplemented(不是 NotImplementedError!)表示不支持该操作,Python 会尝试反向方法;② 定义了 __eq__ 后,__hash__ 会被设为 None,实例变为不可哈希,需要同时定义。
5. 流程控制 5.1 条件语句 1 2 3 4 5 6 7 8 9 10 score = 85 if score >= 90 : grade = "A" elif score >= 80 : grade = "B" elif score >= 60 : grade = "C" else : grade = "D"
三元表达式嵌套的陷阱 :
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 grade = "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 60 else "D" grade_map = { "A" : lambda s: s >= 90 , "B" : lambda s: s >= 80 , "C" : lambda s: s >= 60 , } grade = next ((g for g, cond in grade_map.items() if cond(score)), "D" ) import bisectbreakpoints = [60 , 80 , 90 ] grades = ["D" , "C" , "B" , "A" ] grade = grades[bisect.bisect_right(breakpoints, score)] handlers = { "create" : handle_create, "update" : handle_update, "delete" : handle_delete, } handler = handlers.get(action, handle_default) handler(data)
链式比较的妙用 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 x = 5 print (1 < x < 10 ) print (1 < x > 0 ) print (0 < x < 10 < 100 ) age = 25 if 18 <= age <= 65 : print ("工作年龄" )
Python 没有 switch 语句 ,但 3.10+ 提供了更强大的 match / case。
面试真题 :Python 中如何替代 switch/case?三元表达式嵌套有什么问题?
易错点 :① 嵌套三元表达式可读性极差,不要在生产代码中使用;② 字典映射模式要求所有分支返回相同类型;③ 链式比较 a < b > c 不是 a < b and b > c and a > c,而是 a < b and b > c。
5.2 循环语句 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 for i in range (5 ): print (i) for idx, value in enumerate (["a" , "b" , "c" ], start=1 ): print (f"{idx} : {value} " ) names = ["Alice" , "Bob" ] ages = [25 , 30 ] for name, age in zip (names, ages): print (f"{name} is {age} " ) n = 5 while n > 0 : print (n) n -= 1 else : print ("循环正常结束(未被 break)" ) for num in range (10 ): if num == 3 : continue if num == 7 : break print (num) else : print ("循环完整执行" )
for-else 的实用场景 :在搜索场景下避免引入标志变量。
1 2 3 4 5 6 7 for n in [4 , 6 , 8 , 9 , 11 , 12 ]: if is_prime(n): print (f"找到素数: {n} " ) break else : print ("没有找到素数" )
迭代器与可迭代对象的区别 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 lst = [1 , 2 , 3 ] it = iter (lst) print (type (it)) print (next (it)) print (next (it)) print (next (it)) print (list (it)) from collections.abc import Iterable, Iteratorprint (isinstance (lst, Iterable)) print (isinstance (lst, Iterator)) print (isinstance (it, Iterator))
itertools 常用函数 :
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 import itertoolsfor i in itertools.count(start=1 , step=2 ): if i > 10 : break print (i) print (list (itertools.repeat("hello" , 3 ))) print (list (itertools.chain([1 , 2 ], [3 , 4 ], [5 ]))) print (list (itertools.islice(range (100 ), 5 , 10 ))) print (list (itertools.product([1 , 2 ], ['a' , 'b' ])))print (list (itertools.permutations([1 , 2 , 3 ], 2 )))print (list (itertools.combinations([1 , 2 , 3 , 4 ], 2 )))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)) print (list (itertools.accumulate([1 , 2 , 3 , 4 ]))) print (list (itertools.accumulate([1 , 2 , 3 , 4 ], initial=0 )))
生成器表达式 vs 列表推导式性能对比 :
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 import syslist_comp = [x**2 for x in range (1000000 )] print (sys.getsizeof(list_comp)) gen_expr = (x**2 for x in range (1000000 )) print (sys.getsizeof(gen_expr)) import timeitlist_time = timeit.timeit( "sum([x**2 for x in range(10000)])" , number=1000 ) gen_time = timeit.timeit( "sum(x**2 for x in range(10000))" , number=1000 )
面试真题 :可迭代对象和迭代器有什么区别?生成器表达式和列表推导式在性能上有什么差异?
易错点 :① 迭代器是一次性的,遍历完后不能重用;② zip 在 Python 3 中返回迭代器,遍历一次后为空;③ 生成器表达式更省内存但不一定更快(生成器有函数调用开销);④ itertools.groupby 要求输入已按分组键排序,否则会创建多个分组。
5.3 模式匹配 match / case(3.10+) 1 2 3 4 5 6 7 8 9 10 11 12 13 def handle_command (command ): match command: case ["load" , filename]: print (f"加载文件: {filename} " ) case ["save" , filename, *options]: print (f"保存文件: {filename} , 选项: {options} " ) case {"type" : "error" , "msg" : message}: print (f"错误: {message} " ) case _: print ("未知命令" ) handle_command(["load" , "data.txt" ]) handle_command({"type" : "error" , "msg" : "连接超时" })
match 支持:
序列解构(列表、元组)
映射解构(字典)
捕获变量、通配符 _
守卫子句(guard):case [x, y] if x == y:
更多模式匹配高级用法 :
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 def describe (value ): match value: case int (n) if n > 0 : return f"正整数: {n} " case int (n): return f"非正整数: {n} " case float (f): return f"浮点数: {f} " case str (s): return f"字符串: {s} " case [x, y]: return f"二元组: ({x} , {y} )" case _: return f"其他类型: {type (value)} " def is_vowel (char ): match char.lower(): case "a" | "e" | "i" | "o" | "u" : return True case _: return False class Point : __match_args__ = ("x" , "y" ) def __init__ (self, x, y ): self .x = x self .y = y def where_is (point ): match point: case Point(x=0 , y=0 ): return "原点" case Point(x=0 ): return "Y 轴上" case Point(y=0 ): return "X 轴上" case Point(): return "其他位置" def process_config (config ): match config: case {"database" : {"host" : host, "port" : port}, "debug" : True }: print (f"调试模式连接: {host} :{port} " ) case {"database" : {"host" : host, "port" : port}}: print (f"连接: {host} :{port} " ) case _: print ("配置格式不正确" )
match/case 与 if-elif 的性能对比 :
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 import timeitdef classify_if (x ): if x == 0 : return "zero" elif x == 1 : return "one" elif x == 2 : return "two" else : return "other" def classify_match (x ): match x: case 0 : return "zero" case 1 : return "one" case 2 : return "two" case _: return "other"
工程建议 :match 适合处理复杂的数据结构模式,简单的条件判断仍用 if/elif。
面试真题 :Python 3.10 的 match/case 和 if/elif 有什么区别?match 支持哪些模式?
易错点 :① case _: 中的 _ 是通配符,不是变量名,不会绑定值;② case [x, y] 匹配的是长度为 2 的序列,case [x, y, *rest] 匹配长度 ≥ 2 的序列;③ 类匹配需要 __match_args__ 或使用关键字参数。
6. 输入输出基础 6.1 print() 深度用法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 print ("2024" , "07" , "20" , sep="-" ) print ("Loading" , end="" ) print ("." , end="" )with open ("log.txt" , "w" ) as f: print ("日志内容" , file=f) print (f"{'Product' :<15 } {'Price' :>10 } " )print (f"{'Apple' :<15 } {5.5 :>10.2 f} " )print (f"{'Banana' :<15 } {3.0 :>10.2 f} " )
flush 参数详解 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import timefor i in range (10 ): print (f"\r进度: {'█' * (i+1 )} {'░' * (9 -i)} {(i+1 )*10 } %" , end="" , flush=True ) time.sleep(0.5 ) import sysprint ("开始处理..." , flush=True )print ("完成!" )
logging 模块入门 :
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 import logginglogging.basicConfig( level=logging.DEBUG, format ="%(asctime)s [%(levelname)s] %(name)s: %(message)s" , datefmt="%Y-%m-%d %H:%M:%S" , filename="app.log" , filemode="a" , ) logging.debug("调试信息,仅开发时可见" ) logging.info("常规信息,如启动/关闭" ) logging.warning("警告,可继续运行但需注意" ) logging.error("错误,功能受影响" ) logging.critical("严重错误,系统可能崩溃" ) logging.basicConfig( level=logging.INFO, format ="%(asctime)s [%(levelname)-8s] %(filename)s:%(lineno)d - %(message)s" , handlers=[ logging.FileHandler("app.log" ), logging.StreamHandler(), ], ) logger = logging.getLogger(__name__) logger.info("模块初始化完成" ) try : risky_operation() except Exception as e: logger.exception("操作失败" )
面试真题 :Python 的 print() 的 flush 参数有什么用?生产环境中应该用 print 还是 logging?
易错点 :① 在管道/重定向场景下,print 默认全缓冲,输出可能延迟;② 生产代码中不要用 print 做日志,应使用 logging 模块;③ logging.basicConfig() 只在第一次调用时生效,后续调用不会更新配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 age_str = input ("请输入年龄: " ) age = int (age_str) def get_int (prompt ): while True : try : return int (input (prompt)) except ValueError: print ("请输入有效的整数!" ) age = get_int("请输入年龄: " )
argparse 命令行参数解析 :
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 import argparsedef main (): parser = argparse.ArgumentParser(description="数据处理工具" ) parser.add_argument("input_file" , help ="输入文件路径" ) parser.add_argument("-o" , "--output" , default="output.txt" , help ="输出文件路径" ) parser.add_argument("-v" , "--verbose" , action="store_true" , help ="详细输出模式" ) parser.add_argument("-n" , "--count" , type =int , default=10 , help ="处理数量" ) parser.add_argument("--mode" , choices=["fast" , "safe" ], default="safe" , help ="处理模式" ) args = parser.parse_args() if args.verbose: print (f"输入: {args.input_file} " ) print (f"输出: {args.output} " ) print (f"模式: {args.mode} " ) if __name__ == "__main__" : main()
文件读写基础 :
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 with open ("data.txt" , "r" , encoding="utf-8" ) as f: content = f.read() with open ("data.txt" , "r" , encoding="utf-8" ) as f: for line in f: process(line.strip()) with open ("data.txt" , "r" , encoding="utf-8" ) as f: lines = f.readlines() with open ("output.txt" , "w" , encoding="utf-8" ) as f: f.write("Hello, World!\n" ) f.writelines(["line1\n" , "line2\n" ]) with open ("output.txt" , "a" , encoding="utf-8" ) as f: f.write("追加的内容\n" )
面试真题 :Python 读取大文件时应该用什么方式?with 语句的作用是什么?
易错点 :① f.read() 读取大文件会撑爆内存,应使用 for line in f 逐行读取;② 不指定 encoding 在 Windows 上默认用 GBK,Linux 上用 UTF-8,导致跨平台问题;③ f.readlines() 也会一次性读取全部内容,大文件慎用。
6.3 字符串格式化方式对比
方式
示例
推荐度
适用场景
f-string
f"Hello, {name}"
⭐⭐⭐
所有新代码
str.format()
"Hello, {}".format(name)
⭐⭐
动态模板、国际化
% 格式化
"Hello, %s" % name
⭐
仅维护旧代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 name = "Alice" age = 30 print (f"{name} 今年 {age} 岁" )print (f"明年 {name} 就是 {age + 1 } 岁了" )print (f"圆周率: {3.14159 :.2 f} " ) print (f"二进制: {42 :b} " ) print (f"千分位: {1234567 :,} " ) print (f"{name:*>10 } " ) print (f"{name:*<10 } " ) print (f"{name:*^10 } " )
f-string 高级用法 :
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 x = 42 y = [1 , 2 , 3 ] print (f"{x = } " ) print (f"{x = :b} " ) print (f"{y = } " ) print (f"{len (y) = } " ) width = 10 precision = 3 value = 3.14159 print (f"{value:{width} .{precision} f}" ) align = ">" fill = "*" print (f"{name:{fill} {align} 10}" ) from datetime import datetimenow = datetime.now() print (f"{now:%Y-%m-%d %H:%M:%S} " ) print (f"{now:%A, %B %d, %Y} " ) print (f"{now:%I:%M %p} " ) num = 1234567.8912 print (f"{num:.2 f} " ) print (f"{num:,.2 f} " ) print (f"{num:.2 e} " ) print (f"{num:.3 %} " ) print (f"{42 :#b} " ) print (f"{42 :#x} " ) print (f"{42 :#o} " ) class Point : def __format__ (self, format_spec ): if format_spec == "r" : return f"({self.x} , {self.y} )" return repr (self ) def __init__ (self, x, y ): self .x = x self .y = y p = Point(3 , 4 ) print (f"{p:r} " ) query = f""" SELECT * FROM users WHERE name = '{name} ' AND age > {age} """ data = {"name" : "Alice" , "age" : 30 } print (f"{data['name' ]} " )
面试真题 :f-string 的 = 语法有什么用?f-string 中如何使用字典的键?f-string 和 str.format() 的性能差异?
易错点 :① f-string 中的表达式在运行时求值,不是编译期常量;② Python 3.11 及以下 f-string 中字典键不能用同类型引号;③ f-string 不能跨行使用反斜杠(\n),需要用变量代替。
新手常见错误 Top 10 以下是 Python 初学者最容易犯的错误,务必牢记:
1. 缩进错误 Python 使用缩进表示代码块,混用空格和 Tab 会导致错误。
1 2 3 4 5 6 7 8 9 def greet (): print ("Hello" ) print ("World" ) def greet (): print ("Hello" ) print ("World" )
建议 :在编辑器中设置”显示不可见字符”,并配置”将 Tab 转换为空格”。
2. 变量未定义就使用 1 2 3 4 5 6 print (count) count = 0 print (count)
3. 列表可变陷阱 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def add_item (item, items=[] ): items.append(item) return items print (add_item(1 )) print (add_item(2 )) def add_item (item, items=None ): if items is None : items = [] items.append(item) return items
4. 循环中修改列表 1 2 3 4 5 6 7 8 9 numbers = [1 , 2 , 3 , 4 , 5 ] for n in numbers: if n % 2 == 0 : numbers.remove(n) print (numbers) numbers = [n for n in numbers if n % 2 != 0 ]
5. 整数除法丢失精度 1 2 3 4 5 6 7 8 9 10 11 print (5 / 2 ) print (5 // 2 ) index = 5 / 2 my_list[index] index = 5 // 2 my_list[index]
6. 误用 is 比较值 1 2 3 4 5 6 7 a = 257 b = 257 print (a is b) print (a == b)
7. 字典键不存在错误 1 2 3 4 5 6 d = {"name" : "Alice" } print (d["age" ]) print (d.get("age" , 0 ))
8. 字符串不可变陷阱 1 2 3 4 5 6 7 8 s = "hello" s[0 ] = "H" s = "H" + s[1 :] s = s.replace("h" , "H" )
9. 浮点数精度问题 1 2 3 4 5 6 7 8 9 print (0.1 + 0.2 == 0.3 ) from decimal import Decimalprint (Decimal('0.1' ) + Decimal('0.2' ) == Decimal('0.3' )) abs ((0.1 + 0.2 ) - 0.3 ) < 1e-9
10. 文件未关闭 1 2 3 4 5 6 7 8 9 f = open ("data.txt" ) data = f.read() with open ("data.txt" ) as f: data = f.read()
常见面试题汇总 Q1:Python 是解释型语言还是编译型语言? 答案 :Python 是解释型语言 ,但准确地说,Python 采用”编译+解释”的混合模式。
执行流程 :
源代码 (.py) 被编译成字节码 (.pyc)
Python 虚拟机(PVM)逐行解释执行字节码
与纯解释型语言的区别 :Python 会缓存字节码,下次执行时如果源码未修改,直接加载 .pyc 文件,提高了启动速度。
Q2:is 和 == 的区别是什么? 答案 :
is 比较对象的身份(内存地址),即两个引用是否指向同一个对象
== 比较对象的值相等性,调用对象的 __eq__ 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 a = [1 , 2 , 3 ] b = [1 , 2 , 3 ] print (a is b) print (a == b) a = 256 b = 256 print (a is b) a = 257 b = 257 print (a is b)
Q3:什么是 Python 的小整数池? 答案 :CPython 将 -5 到 256 的整数预先创建并缓存,这些整数是单例的,可以安全地用 is 比较。
目的 :减少内存分配,提高性能。
1 2 3 4 5 6 7 a = 256 b = 256 print (a is b) a = 257 b = 257 print (a is b)
Q4:深拷贝和浅拷贝的区别? 答案 :
浅拷贝 :创建新对象,但对内部子对象仍是引用(copy.copy() 或 list[:])
深拷贝 :递归复制所有子对象,完全独立(copy.deepcopy())
1 2 3 4 5 6 7 8 9 10 11 import copyoriginal = [[1 , 2 ], [3 , 4 ]] shallow = copy.copy(original) shallow[0 ][0 ] = 99 print (original) deep = copy.deepcopy(original) deep[0 ][0 ] = 100 print (original)
Q5:Python 中的变量是什么? 答案 :Python 中的变量是对象的引用 ,不是”盒子”。
1 2 3 4 5 6 a = [1 , 2 , 3 ] b = a b.append(4 ) print (a)
Q6:*args 和 **kwargs 的作用? 答案 :
*args:收集位置参数为元组
**kwargs:收集关键字参数为字典
1 2 3 4 5 def func (*args, **kwargs ): print (args) print (kwargs) func(1 , 2 , 3 , a=4 , b=5 )
Q7:Python 中如何实现多行注释? 答案 :Python 没有原生的多行注释语法,通常使用三引号字符串作为”伪注释”:
1 2 3 4 5 """ 这是多行字符串, 可以用作文档字符串(docstring), 也可以当作多行注释使用。 """
Q8:pass 语句的作用? 答案 :pass 是空操作占位符,用于语法上需要语句但不需要执行任何操作的场景。
1 2 3 4 5 def not_implemented (): pass class EmptyClass : pass
Q9:Python 3 相比 Python 2 的重要变化? 答案 :
print 从语句变为函数
整数除法:/ 返回浮点数,// 返回整数
默认字符串为 Unicode
range 返回迭代器,不再返回列表
新增 async/await 语法
类型注解支持
Q10:如何判断一个对象是否可迭代? 答案 :
1 2 3 4 5 6 7 from collections.abc import Iterableprint (isinstance ([1 , 2 , 3 ], Iterable)) print (isinstance (123 , Iterable)) print (hasattr ([1 , 2 , 3 ], '__iter__' ))
实战项目:命令行计算器 通过一个完整的小项目,综合运用本阶段学习的知识点。
项目目标 创建一个支持基本运算的命令行计算器,具备以下功能:
加、减、乘、除四则运算
支持连续计算
友好的错误提示
输入 quit 退出程序
完整代码 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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 """ 命令行计算器 - 第一阶段实战项目 知识点:变量、运算符、控制流、函数、异常处理、字符串格式化 """ def add (a, b ): """加法""" return a + b def subtract (a, b ): """减法""" return a - b def multiply (a, b ): """乘法""" return a * b def divide (a, b ): """除法""" if b == 0 : raise ValueError("除数不能为零" ) return a / b def calculate (a, op, b ): """根据运算符执行计算""" operations = { '+' : add, '-' : subtract, '*' : multiply, '/' : divide } if op not in operations: raise ValueError(f"不支持的运算符: {op} " ) return operations[op](a, b) def get_number (prompt ): """获取用户输入的数字""" while True : try : return float (input (prompt)) except ValueError: print ("请输入有效的数字!" ) def get_operator (): """获取用户输入的运算符""" while True : op = input ("请输入运算符 (+, -, *, /): " ).strip() if op in ('+' , '-' , '*' , '/' ): return op print ("请输入有效的运算符: +, -, *, /" ) def main (): """主函数""" print ("=" * 40 ) print (" 命令行计算器 v1.0" ) print (" 输入 'quit' 或 'q' 退出程序" ) print ("=" * 40 ) history = [] while True : print ("\n" + "-" * 30 ) first_input = input ("请输入第一个数字 (或输入 quit 退出): " ).strip() if first_input.lower() in ('quit' , 'q' ): break try : a = float (first_input) except ValueError: print ("请输入有效的数字!" ) continue op = get_operator() b = get_number("请输入第二个数字: " ) try : result = calculate(a, op, b) print (f"\n结果: {a} {op} {b} = {result} " ) history.append(f"{a} {op} {b} = {result} " ) except ValueError as e: print (f"错误: {e} " ) if history: print ("\n" + "=" * 40 ) print ("本次计算历史:" ) for i, record in enumerate (history, 1 ): print (f" {i} . {record} " ) print ("\n感谢使用,再见!" ) if __name__ == "__main__" : main()
运行示例 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 ======================================== 命令行计算器 v1.0 输入 'quit' 或 'q' 退出程序 ======================================== ------------------------------ 请输入第一个数字 (或输入 quit 退出): 10 请输入运算符 (+, -, *, /): + 请输入第二个数字: 5 结果: 10.0 + 5.0 = 15.0 ------------------------------ 请输入第一个数字 (或输入 quit 退出): 100 请输入运算符 (+, -, *, /): / 请输入第二个数字: 0 错误: 除数不能为零 ------------------------------ 请输入第一个数字 (或输入 quit 退出): quit ======================================== 本次计算历史: 1. 10.0 + 5.0 = 15.0 感谢使用,再见!
知识点覆盖
功能
涉及知识点
函数定义
def、参数、返回值、文档字符串
运算符
算术运算符、比较运算符
控制流
while、if、break、continue
异常处理
try/except、raise
字符串
input()、strip()、lower()、f-string
数据结构
字典映射运算符、列表存储历史
模块入口
if __name__ == "__main__"
进阶练习 尝试为计算器添加以下功能:
支持取模(%)和幂运算(**)
支持科学计数法输入(如 1e10)
添加 history 命令查看历史
支持从文件读取计算表达式
添加 help 命令显示帮助信息
附录:第一阶段自检清单 在继续学习第二阶段之前,请确保你能:
工程师寄语 :基础语法的熟练程度决定了你写代码的”手感”。不要急于求成,多写、多错、多总结。Python 的优雅不在于语法本身,而在于用简洁的代码表达清晰的意图。