Java开发者转战Python:零基础快速入门
# 一、Python简介与环境搭建
# 1、Python的发展历程与特点
作为Java开发者,你可能听过这样一个段子:"人生苦短,我用Python"。这不仅仅是一句口号,而是Python简洁高效的真实写照。
# 1.1、Python的历史演进
Python由Guido van Rossum在1989年圣诞节期间创造,初衷是开发一门既简单又强大的编程语言。让我们快速了解它的演进历程:
- Python 1.0(1994年):确立了核心设计理念,引入lambda、map、filter等函数式编程特性
- Python 2.x(2000-2010年):逐渐流行,成为科学计算和Web开发的重要工具。最后版本是Python 2.7(2010年)
- Python 3.x(2008年至今):彻底重新设计,修正了许多历史遗留问题,但不向后兼容Python 2
需要注意的是,Python 2已经在2020年1月1日正式停止维护。如果你是现在开始学习Python,直接学Python 3就好,完全不用纠结Python 2。
# 1.2、3的重大改进
作为Java开发者,你会欣赏Python 3带来的这些改进:
- 统一的Unicode字符串处理:所有字符串默认Unicode,告别Java早期的字符编码噩梦
- 更清晰的整数除法:
3/2得到1.5而不是1,整除使用//操作符 - 改进的异常语法:类似Java的
catch语法,except Exception as e更直观 - 类型注解支持(Python 3.5+):让习惯静态类型的Java开发者更舒适
- 异步编程原生支持(Python 3.5+):
async/await语法,类似Java的CompletableFuture
# 1.3、Python的设计哲学
在Python交互环境中输入import this,你会看到"The Zen of Python"(Python之禅)。这是Python的设计理念,其中几条特别值得Java开发者关注:
优美胜于丑陋(Beautiful is better than ugly)
明了胜于晦涩(Explicit is better than implicit)
简洁胜于复杂(Simple is better than complex)
可读性很重要(Readability counts)
对比Java冗长的样板代码,Python追求的是用最少的代码表达最清晰的意图。比如创建一个Person类:
// Java写法
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
# Python写法(使用dataclass)
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
看到差距了吧?Python只用4行代码就实现了Java需要20多行完成的功能。
# 1.4、Python的核心优势
简洁易读的语法:用缩进代替花括号,代码结构一目了然。习惯了Java的你可能一开始不适应,但很快就会爱上这种清爽的感觉
丰富的标准库和第三方生态:Python自带"电池"(batteries included),标准库几乎覆盖了日常开发的各个方面。再加上PyPI(Python Package Index)上50万+的第三方包,你几乎总能找到现成的轮子
动态类型与解释执行:无需编译,写完即跑。这对快速原型开发和脚本编写特别友好。当然,你也可以用类型注解+静态检查工具来获得类型安全
跨平台兼容性:写一次,到处运行——这次是真的。不像Java还需要考虑不同JVM的差异,Python的跨平台能力更加彻底
多范式编程支持:面向对象、函数式、过程式——想怎么写就怎么写。Python不像Java那样强制面向对象,你可以根据场景灵活选择编程范式
# 2、Python与Java的核心差异
从Java转到Python,最大的感受就是"轻装上阵"。但轻装不代表简陋,让我们看看两者的核心差异。
# 2.1、运行机制差异
编译型 vs 解释型
// Java: 编译后运行
javac HelloWorld.java // 生成 .class字节码
java HelloWorld // JVM执行字节码
# Python: 直接解释执行
python hello_world.py # 解释器直接运行
Java是典型的编译型语言(严格说是先编译成字节码再由JVM解释执行),而Python是解释型语言。这带来几个直接影响:
- 开发速度:Python改完代码直接跑,无需编译环节,迭代更快
- 运行性能:Java通常比Python快3-10倍(但对大多数Web应用来说,性能瓶颈在I/O而非计算)
- 错误发现:Java在编译期就能发现类型错误,Python要到运行时才暴露(但可以用类型检查工具弥补)
# 2.2、类型系统差异
静态类型 vs 动态类型
这是最核心的差异之一:
// Java: 静态类型,变量类型编译期确定
String name = "张三";
name = 123; // 编译错误!
# Python: 动态类型,变量只是对象的引用
name = "张三"
name = 123 # 完全合法,name现在指向整数对象
动态类型的优势:
- 代码更简洁,不需要写类型声明
- 灵活性更高,鸭子类型(duck typing)让多态更自然
动态类型的劣势:
- 类型错误要到运行时才暴露
- IDE的代码补全和重构能力相对较弱
好消息:Python 3.5+引入了类型注解,可以两全其美:
def greet(name: str) -> str:
return f"Hello, {name}"
# 配合mypy工具做静态检查
result: int = greet("张三") # mypy会警告类型不匹配
显式类型声明 vs 类型推断
// Java需要显式声明
Map<String, List<Integer>> data = new HashMap<>();
# Python直接推断
data = {} # 空字典
data = {"scores": [95, 87, 92]} # 自动推断为dict[str, list[int]]
Python的类型推断让代码更简洁,但如果你习惯了Java的明确性,也可以加上类型注解:
from typing import Dict, List
data: Dict[str, List[int]] = {"scores": [95, 87, 92]}
# 2.3、性能与内存管理
性能特性对比
| 维度 | Java | Python |
|---|---|---|
| 启动速度 | 较慢(JVM启动开销) | 快(直接解释执行) |
| 运行速度 | 快(JIT编译优化) | 较慢(解释执行) |
| 内存占用 | 较大(JVM堆) | 较小 |
| 多线程 | 真正的并行 | GIL限制(后面会讲) |
实际项目中,Python的"慢"往往没想象中那么严重,因为:
- 性能瓶颈通常在I/O而非CPU
- 可以用C扩展加速关键代码(NumPy、Pandas底层都是C)
- 开发效率的提升能弥补运行效率的损失
内存管理机制
// Java: 垃圾回收(GC)
Object obj = new Object();
obj = null; // 等待GC回收
# Python: 引用计数 + 垃圾回收
obj = SomeClass()
obj = None # 引用计数为0,立即回收
Python使用引用计数作为主要内存管理机制,配合周期性垃圾回收处理循环引用。这意味着:
- 对象销毁更及时(引用计数归零即回收)
- 但循环引用需要GC介入
- 可以用
with语句明确管理资源生命周期
# 2.4、代码组织与语法风格
代码组织方式
// Java: 强制面向对象,一切皆类
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
}
# Python: 模块化,更灵活
# 可以直接写函数,不需要类
def add(a, b):
return a + b
# 当然也可以用类
class MathUtils:
@staticmethod
def add(a, b):
return a + b
Python的模块就是文件,不需要像Java那样包名和目录结构完全对应。一个简单的工具函数,Python可以直接写在模块级别,而Java必须套上一个类。
小结对比表
| 特性 | Java | Python | 迁移建议 |
|---|---|---|---|
| 类型系统 | 静态 | 动态(可选注解) | 复杂项目建议用类型注解 |
| 运行方式 | 编译+解释 | 直接解释 | 习惯热重载的快感 |
| 性能 | 高 | 中等 | 瓶颈处考虑用C扩展 |
| 语法风格 | 冗长严谨 | 简洁灵活 | 拥抱简洁,但注意规范 |
| 并发模型 | 多线程友好 | 多进程为主 | 了解GIL的影响 |
| 学习曲线 | 陡峭 | 平缓 | 但精通同样需要时间 |
# 3、Python环境搭建
对Java开发者来说,Python的环境搭建比JDK简单多了——没有那么多版本兼容性的坑,也不需要配置CLASSPATH。
# 3.1、Python安装
截至2024年,Python 3.13是最新的稳定版本。不过实际项目中,Python 3.9+ 都是不错的选择。
Windows安装
- 访问 python.org (opens new window) 下载Windows安装包
- 重要:安装时勾选"Add Python to PATH",这样就不用手动配置环境变量了
- 建议选择"Custom Installation",可以选择安装位置和组件
# 验证安装
python --version # 输出: Python 3.13.x
pip --version # pip是Python的包管理器,类似Maven
macOS安装
推荐使用Homebrew,比官方安装包更方便管理:
# 安装Homebrew(如果没有)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 安装Python
brew install python@3.13
# 验证
python3 --version
pip3 --version
Linux安装
大多数现代Linux发行版都预装了Python,但可能不是最新版本:
# Ubuntu/Debian
sudo apt update
sudo apt install python3.13 python3.13-pip python3.13-venv
# CentOS/RHEL/Fedora
sudo dnf install python3.13 python3.13-pip
# 验证
python3.13 --version
# 3.2、虚拟环境管理
Java开发者对依赖冲突应该不陌生(不同版本的同一个jar包)。Python用虚拟环境解决这个问题,类似于Docker但更轻量。
为什么需要虚拟环境?
想象一下这个场景:
- 项目A需要Django 3.2
- 项目B需要Django 4.0
- 全局只能安装一个版本...
虚拟环境让每个项目都有独立的Python环境,完美解决版本冲突。
venv——Python官方方案
# 创建虚拟环境(类似创建一个独立的Python安装)
python -m venv myproject_env
# Windows激活
myproject_env\Scripts\activate
# macOS/Linux激活
source myproject_env/bin/activate
# 激活后,命令行前缀会显示环境名
(myproject_env) $ python --version
# 退出虚拟环境
deactivate
virtualenv——第三方增强版
功能更丰富,支持更多Python版本:
# 安装
pip install virtualenv
# 创建环境(可以指定Python版本)
virtualenv -p python3.9 myproject_env
# 使用方式和venv相同
conda——数据科学界的瑞士军刀
如果你要做数据科学或机器学习,推荐conda。它不仅管理Python包,还能管理C++库、R包等:
# 安装Miniconda(轻量版)或Anaconda(完整版)
# 下载地址: https://conda.io/
# 创建环境
conda create -n myproject python=3.13
# 激活
conda activate myproject
# 安装包(conda会自动解决依赖)
conda install numpy pandas matplotlib
# 列出环境
conda env list
# 3.3、IDE与编辑器选择
从Java生态转来,你需要选择趁手的开发工具:
PyCharm——Python界的IntelliJ IDEA
JetBrains出品,界面和快捷键与IDEA类似,适合Java开发者:
- 优点:智能代码补全、强大的调试、重构工具、Git集成
- 缺点:比较重,启动慢
- 适合:大型项目,团队开发
主要功能:
- 智能代码补全和错误检测
- 强大的调试器(类似IDEA的调试体验)
- 内置终端和数据库工具
- 版本控制集成
VS Code——微软的轻量选择
- 优点:启动快,插件丰富,内存占用小
- 缺点:需要配置插件才能获得完整体验
- 适合:快速开发,多语言项目
推荐插件:
- Python(微软官方)
- Pylance(类型检查和智能提示)
- Python Docstring Generator
- GitLens
Jupyter Notebook——交互式开发
特别适合数据分析和机器学习:
# 安装
pip install jupyter
# 启动
jupyter notebook
# 浏览器会自动打开 http://localhost:8888
Jupyter的优势:
- 代码、文档、图表混合编写
- 逐个单元格执行代码
- 可视化结果直接展示
- 支持Markdown文档
# 3.4、pip包管理器
pip是Python的标准包管理器,类似Maven但更简单:
pip基础命令
# 安装包
pip install requests
# 安装特定版本
pip install django==4.0
# 安装最新版本
pip install --upgrade numpy
# 卸载
pip uninstall requests
# 列出已安装的包
pip list
# 显示包信息
pip show requests
# 搜索包(已废弃,建议去PyPI网站搜索)
requirements.txt——依赖管理文件
类似于Maven的pom.xml,但更简洁:
# 生成当前环境的依赖列表
pip freeze > requirements.txt
# requirements.txt内容示例:
# Django==4.2.0
# requests==2.31.0
# numpy==1.24.3
# 在新环境中安装所有依赖
pip install -r requirements.txt
国内镜像源配置——告别龟速下载
默认pip从国外源下载,速度感人。配置国内镜像源:
临时使用:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ requests
永久配置(推荐):
Windows: 在 %APPDATA%\pip\pip.ini 文件中添加:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple/
trusted-host = pypi.tuna.tsinghua.edu.cn
macOS/Linux: 在 ~/.pip/pip.conf 文件中添加:
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple/
trusted-host = pypi.tuna.tsinghua.edu.cn
常用国内镜像源:
- 清华大学:
https://pypi.tuna.tsinghua.edu.cn/simple/ - 阿里云:
https://mirrors.aliyun.com/pypi/simple/ - 腾讯云:
https://mirrors.cloud.tencent.com/pypi/simple/
快速上手检验
安装完成后,让我们写第一个Python程序:
# hello.py
def greet(name):
return f"Hello, {name}! 欢迎来到Python世界!"
if __name__ == "__main__":
print(greet("Java开发者"))
print("Python安装成功! 🎉")
运行:
python hello.py
如果看到输出,恭喜你,Python环境搭建成功!
小贴士:
- 建议每个项目都用独立的虚拟环境
- requirements.txt要纳入版本控制
- 定期更新pip本身:
python -m pip install --upgrade pip - 如果网络不稳定,可以考虑使用conda替代pip
# 二、Python基础语法
# 1、变量与数据类型
从Java转到Python,第一个惊喜就是:不用写类型声明了!但这不代表Python没有类型,只是更加灵活。
# 1.1、变量声明与命名规范
Python的变量声明
// Java: 必须声明类型
int age = 25;
String name = "张三";
# Python: 直接赋值即可
age = 25
name = "张三"
Python的变量本质上是对象的引用(类似Java的引用类型),赋值时会自动推断类型。
命名规范
Python和Java的命名风格有些差异:
# Python风格 - 推荐使用
user_name = "张三" # 变量和函数:snake_case(下划线命名)
MAX_SIZE = 100 # 常量:全大写+下划线
class UserProfile: # 类名:PascalCase(大驼峰)
pass
# Java风格 - 在Python中不推荐
userName = "张三" # camelCase在Python不常用
命名规则:
- 只能包含字母、数字、下划线
- 不能以数字开头
- 区分大小写(
name和Name是不同变量) - 不能使用Python关键字(
if、for、class等)
特殊命名约定:
_internal_var = "私有变量" # 单下划线开头:内部使用(约定,非强制)
__private_var = "更私有" # 双下划线开头:名称改写(避免子类冲突)
__magic__ = "魔术方法" # 双下划线前后:1、Python内置特殊方法-魔法方法,用于自定义类的行为;2、特殊内置属性,由解释器自动设置
# 1.2、基本数据类型
Python的类型系统比Java简单,但该有的都有。
数值类型
# int - 整数(没有大小限制!)
age = 25
big_num = 123456789012345678901234567890 # Java的long都装不下
# float - 浮点数
price = 99.99
scientific = 1.5e10 # 科学计数法:1.5 × 10^10
# complex - 复数(Java没有原生支持)
z = 3 + 4j
print(z.real) # 输出: 3.0
print(z.imag) # 输出: 4.0
对比Java:
// Java需要考虑大小
int age = 25; // 32位
long bigNum = 123456L; // 64位
BigInteger huge = new BigInteger("123..."); // 任意大小
double price = 99.99; // 64位
float f = 99.99f; // 32位
布尔类型
# bool - 注意首字母大写!
is_active = True # 不是true
is_valid = False # 不是false
# 布尔运算
result = True and False # 使用 and,不是 &&
result = True or False # 使用 or,不是 ||
result = not True # 使用 not,不是 !
Python的"真值"(Truthy/Falsy)
这是Python的独特之处,以下值会被当作False:
# 这些都是"假"
bool(0) # False
bool(0.0) # False
bool("") # False - 空字符串
bool([]) # False - 空列表
bool({}) # False - 空字典
bool(None) # False
# 其他基本都是"真"
bool(1) # True
bool("hello") # True
bool([1, 2]) # True
这让条件判断更简洁:
# Python风格
if user_list: # 直接判断,空列表为False
process(user_list)
# 不需要像Java那样
# if (userList != null && !userList.isEmpty()) { ... }
字符串
# str - 字符串(不可变类型)
name = "张三"
message = 'Hello'
关于字符串的更多内容,我们在下一节详细讲解。
None类型
# None - 相当于Java的null
result = None
# 判断None
if result is None: # 推荐用 is
print("没有结果")
if result == None: # 也可以,但不推荐
print("没有结果")
对比Java:
String result = null;
if (result == null) {
System.out.println("没有结果");
}
# 1.3、类型转换与类型检查
类型转换
# 显式转换
age_str = "25"
age = int(age_str) # 字符串 → 整数
price = 99.99
price_int = int(price) # 浮点 → 整数(截断,不是四舍五入)
num = 100
num_str = str(num) # 整数 → 字符串
num_float = float(num) # 整数 → 浮点
# 转换失败会抛异常
try:
int("abc") # ValueError
except ValueError as e:
print(f"转换失败: {e}")
类型检查
# type() - 获取类型
age = 25
print(type(age)) # <class 'int'>
print(type(age) == int) # True
# isinstance() - 类型检查(推荐,支持继承)
age = 25
print(isinstance(age, int)) # True
print(isinstance(age, (int, float))) # True - 检查多个类型
# 对比Java
# if (obj instanceof String) { ... }
类型注解(Type Hints)
Python 3.5+支持类型注解,让Java开发者更舒适:
# 加上类型注解
def greet(name: str, age: int) -> str:
return f"Hello, {name}! You are {age} years old."
# 变量类型注解
user_name: str = "张三"
score: float = 95.5
users: list[str] = ["张三", "李四"]
# 注意:这只是"注解",运行时不会检查!
result = greet("张三", "25") # 传了字符串,不会报错
# 需要配合mypy等工具做静态检查
# 1.4、与Java的类型系统对比
| 特性 | Java | Python |
|---|---|---|
| 类型声明 | 强制 | 可选(类型注解) |
| 基本类型 | 有(int, double等) | 无,一切皆对象 |
| 包装类型 | 有(Integer, Double等) | 无需区分 |
| 整数范围 | 固定(int 32位, long 64位) | 任意大小 |
| 字符类型 | 有(char) | 无,用单字符字符串 |
| 空值 | null | None |
| 类型检查时机 | 编译期 | 运行期(可用工具静态检查) |
实战建议:
- 小脚本可以不写类型注解,追求快速开发
- 大项目建议加上类型注解,配合mypy检查
- 利用Python的"鸭子类型",关注对象行为而非类型
- 善用
isinstance()做运行时类型检查
小技巧:
# 多重赋值
x, y, z = 1, 2, 3
# 变量交换(Java需要中间变量)
a, b = b, a
# 链式比较(Java做不到)
age = 25
if 18 <= age < 60: # 相当于 age >= 18 and age < 60
print("工作年龄段")
# 2、字符串操作
字符串是编程中最常用的数据类型,Python的字符串操作比Java简洁太多了。
# 2.1、字符串定义方式
单引号、双引号、三引号
# 单引号和双引号完全等价
name1 = 'Python'
name2 = "Python"
print(name1 == name2) # True
# 优势:可以互相嵌套
message1 = "He said 'Hello'"
message2 = 'She replied "Hi"'
# 三引号:多行字符串(相当于Java的文本块)
long_text = """
这是一段
多行文本
可以随意换行
"""
sql = '''
SELECT * FROM users
WHERE age > 18
AND status = 'active'
'''
对比Java(Java 15+才支持文本块):
// Java 15+
String sql = """
SELECT * FROM users
WHERE age > 18
AND status = 'active'
""";
# 2.2、字符串格式化
Python有三种字符串格式化方式,推荐使用f-string(最现代、最简洁)。
% 格式化(老式写法)
name = "张三"
age = 25
# 类似C语言的printf
message = "我叫%s,今年%d岁" % (name, age)
print(message) # 我叫张三,今年25岁
# 格式控制
price = 99.999
print("价格:%.2f元" % price) # 价格:100.00元
str.format()(Python 2.6+)
name = "张三"
age = 25
# 位置参数
message = "我叫{},今年{}岁".format(name, age)
# 索引参数
message = "我叫{0},今年{1}岁,{0}很高兴认识你".format(name, age)
# 命名参数(推荐,可读性好)
message = "我叫{name},今年{age}岁".format(name=name, age=age)
# 格式控制
price = 99.999
print("价格:{:.2f}元".format(price)) # 价格:100.00元
# 填充和对齐
print("{:>10}".format("右对齐")) # " 右对齐"
print("{:<10}".format("左对齐")) # "左对齐 "
print("{:^10}".format("居中")) # " 居中 "
print("{:0>5}".format("42")) # "00042"
f-string(Python 3.6+,强烈推荐!)
name = "张三"
age = 25
score = 95.678
# 最简洁的方式
message = f"我叫{name},今年{age}岁"
# 可以直接在{}中写表达式
print(f"明年我{age + 1}岁")
print(f"成绩:{score:.2f}分") # 成绩:95.68分
# 支持复杂表达式
users = ["张三", "李四", "王五"]
print(f"共有{len(users)}个用户")
# 可以调用方法
text = "hello"
print(f"大写:{text.upper()}") # 大写:HELLO
# 多行f-string
message = (
f"姓名:{name}\n"
f"年龄:{age}\n"
f"成绩:{score:.2f}"
)
# Python 3.8+ 支持 = 调试
x = 10
print(f"{x=}") # x=10(显示变量名和值,调试利器)
对比Java:
// Java传统方式
String message = String.format("我叫%s,今年%d岁", name, age);
// Java 15+ 文本块 + formatted()
String message = """
姓名:%s
年龄:%d
""".formatted(name, age);
# 2.3、常用字符串方法
Python的字符串方法非常丰富,而且都返回新字符串(字符串是不可变的)。
大小写转换
text = "Hello World"
print(text.upper()) # HELLO WORLD
print(text.lower()) # hello world
print(text.capitalize()) # Hello world(首字母大写)
print(text.title()) # Hello World(每个单词首字母大写)
print(text.swapcase()) # hELLO wORLD(大小写互换)
查找和判断
text = "Hello Python"
# 查找子串
print(text.find("Python")) # 6(返回索引)
print(text.find("Java")) # -1(找不到)
print(text.index("Python")) # 6(找不到会抛异常)
# 判断
print(text.startswith("Hello")) # True
print(text.endswith("Python")) # True
print("123".isdigit()) # True(是否全是数字)
print("abc".isalpha()) # True(是否全是字母)
print("abc123".isalnum()) # True(是否全是字母或数字)
print(" ".isspace()) # True(是否全是空白字符)
替换和分割
text = "Hello Python Python"
# 替换
print(text.replace("Python", "Java")) # Hello Java Java
print(text.replace("Python", "Java", 1)) # Hello Java Python(只替换1次)
# 分割
csv = "张三,25,北京"
parts = csv.split(",")
print(parts) # ['张三', '25', '北京']
# 多行分割
text = "第一行\n第二行\n第三行"
lines = text.splitlines()
print(lines) # ['第一行', '第二行', '第三行']
# 连接(反向操作)
words = ["Hello", "Python", "World"]
result = " ".join(words)
print(result) # Hello Python World
对比Java:
// Java需要更多代码
String[] parts = csv.split(",");
String result = String.join(" ", words);
去除空白
text = " Hello Python "
print(text.strip()) # "Hello Python"(去除两端空白)
print(text.lstrip()) # "Hello Python "(去除左侧)
print(text.rstrip()) # " Hello Python"(去除右侧)
# 去除指定字符
url = "https://example.com/"
print(url.rstrip("/")) # https://example.com
# 2.4、字符串切片操作
这是Python的特色功能,比Java的substring()灵活多了!
基本切片
text = "Python"
# 语法:[start:end:step]
print(text[0]) # P(单个字符)
print(text[0:3]) # Pyt(索引0到2,不包含3)
print(text[:3]) # Pyt(省略start,从头开始)
print(text[3:]) # hon(省略end,到末尾)
print(text[:]) # Python(完整复制)
# 负索引(从右往左数)
print(text[-1]) # n(最后一个字符)
print(text[-3:]) # hon(最后3个字符)
print(text[:-1]) # Pytho(除了最后一个)
# 步长
print(text[::2]) # Pto(每隔一个取一个)
print(text[::-1]) # nohtyP(反转字符串!)
对比Java:
// Java需要用substring
String text = "Python";
String sub = text.substring(0, 3); // Pyt
// 反转需要StringBuilder
String reversed = new StringBuilder(text).reverse().toString();
切片的实用技巧
# 去除文件扩展名
filename = "document.pdf"
name = filename[:-4] # document
# 提取域名
email = "user@example.com"
domain = email[email.index("@")+1:] # example.com
# 每隔N个字符插入分隔符
card = "1234567890123456"
formatted = "-".join([card[i:i+4] for i in range(0, len(card), 4)])
print(formatted) # 1234-5678-9012-3456
# 2.5、原始字符串与转义字符
转义字符
# 常见转义字符
print("第一行\n第二行") # 换行
print("姓名:\t张三") # 制表符
print("路径:C:\\Users") # 反斜杠
print("他说:\"你好\"") # 双引号
原始字符串(raw string)
# 正则表达式和Windows路径特别有用
path = r"C:\Users\张三\Documents" # 不需要转义
regex = r"\d+\.\d+" # 正则表达式
# 对比普通字符串
path_normal = "C:\\Users\\张三\\Documents" # 需要双反斜杠
对比Java:
// Java也需要转义
String path = "C:\\Users\\张三\\Documents";
// Java 15+ 支持原始字符串字面量(预览特性)
Unicode字符
# Unicode字符
print("\u4e2d\u6587") # 中文
print("\U0001F600") # 😀(emoji)
# Python 3默认UTF-8,直接写中文和emoji
message = "你好 🎉"
字符串的不可变性
text = "Python"
# text[0] = "J" # TypeError: 'str' object does not support item assignment
# 需要创建新字符串
text = "J" + text[1:] # "Jython"
# 或者用replace
text = text.replace("P", "J")
# 2.6、字符串性能提示
拼接大量字符串
# ❌ 错误方式:在循环中用+拼接(O(n²)复杂度)
result = ""
for i in range(1000):
result += str(i)
# ✅ 正确方式:用join(O(n)复杂度)
result = "".join(str(i) for i in range(1000))
# ✅ 或者用列表积累再join
parts = []
for i in range(1000):
parts.append(str(i))
result = "".join(parts)
对比Java:
// Java也是同样的道理
// ❌ String result = "";
// for (int i = 0; i < 1000; i++) result += i;
// ✅ StringBuilder sb = new StringBuilder();
// for (int i = 0; i < 1000; i++) sb.append(i);
// String result = sb.toString();
小结对比表
| 操作 | Java | Python |
|---|---|---|
| 格式化 | String.format() | f-string(推荐) |
| 分割 | split() | split() |
| 连接 | String.join() | str.join() |
| 替换 | replace() | replace() |
| 切片 | substring() | [start:end] |
| 反转 | StringBuilder.reverse() | [::-1] |
| 多行文本 | """..."""(Java 15+) | """...""" |
Python的字符串操作更简洁直观,尤其是f-string和切片语法,能大大提高开发效率!
# 3、运算符
Python的运算符和Java大部分相似,但也有一些独特的地方,特别是成员运算符和身份运算符。
# 3.1、算术运算符
a = 10
b = 3
print(a + b) # 13 - 加法
print(a - b) # 7 - 减法
print(a * b) # 30 - 乘法
print(a / b) # 3.333... - 除法(注意:总是返回浮点数!)
print(a // b) # 3 - 整除(向下取整)
print(a % b) # 1 - 取模
print(a ** b) # 1000 - 幂运算(10的3次方)
重点差异:
// Java
int a = 10, b = 3;
System.out.println(a / b); // 3(整数除法)
# Python
a, b = 10, 3
print(a / b) # 3.3333...(总是浮点除法)
print(a // b) # 3(整除)
幂运算是Python的特色:
# Python有专门的幂运算符
print(2 ** 10) # 1024
# Java需要用Math.pow()
# Math.pow(2, 10)
复合赋值:
x = 10
x += 5 # x = x + 5
x -= 3 # x = x - 3
x *= 2 # x = x * 2
x /= 4 # x = x / 4
x //= 2 # x = x // 2
x %= 3 # x = x % 3
x **= 2 # x = x ** 2
# 3.2、比较运算符
a = 10
b = 20
print(a == b) # False - 等于
print(a != b) # True - 不等于
print(a > b) # False - 大于
print(a < b) # True - 小于
print(a >= b) # False - 大于等于
print(a <= b) # True - 小于等于
链式比较(Python独有,超级实用!):
age = 25
# Python可以这样写
if 18 <= age < 60:
print("工作年龄段")
# 相当于(但更简洁)
if age >= 18 and age < 60:
print("工作年龄段")
# 多重比较
x = 5
if 1 < x < 10 < 100: # 完全合法!
print("x在1到10之间,10小于100")
对比Java:
// Java不支持链式比较
if (age >= 18 && age < 60) {
System.out.println("工作年龄段");
}
# 3.3、逻辑运算符
# Python使用英文单词,更易读
a = True
b = False
print(a and b) # False - 逻辑与(Java用&&)
print(a or b) # True - 逻辑或(Java用||)
print(not a) # False - 逻辑非(Java用!)
短路求值:
# and:如果第一个为False,不会计算第二个
def expensive_check():
print("执行了昂贵的检查")
return True
x = False
if x and expensive_check(): # expensive_check()不会执行
print("都为真")
# or:如果第一个为True,不会计算第二个
x = True
if x or expensive_check(): # expensive_check()不会执行
print("至少一个为真")
逻辑运算的返回值(Python的独特之处):
# and返回第一个假值,或最后一个值
print(5 and 10) # 10(都为真,返回最后一个)
print(0 and 10) # 0(第一个为假)
print("" and "abc") # ""(空字符串为假)
# or返回第一个真值,或最后一个值
print(5 or 10) # 5(第一个为真)
print(0 or 10) # 10(第一个为假,返回第二个)
print("" or "abc") # "abc"
# 实用技巧:提供默认值
name = input_name or "匿名用户" # 如果input_name为空,使用默认值
# 3.4、位运算符
与Java完全相同:
a = 60 # 0011 1100
b = 13 # 0000 1101
print(a & b) # 12 (0000 1100) - 按位与
print(a | b) # 61 (0011 1101) - 按位或
print(a ^ b) # 49 (0011 0001) - 按位异或
print(~a) # -61 - 按位取反
print(a << 2) # 240 (1111 0000) - 左移
print(a >> 2) # 15 (0000 1111) - 右移
# 3.5、成员运算符(Python独有)
这是Python的特色,超级好用!
# in - 检查元素是否在序列中
fruits = ["苹果", "香蕉", "橙子"]
print("苹果" in fruits) # True
print("葡萄" in fruits) # False
# not in - 检查元素是否不在序列中
print("葡萄" not in fruits) # True
# 适用于各种容器
text = "Hello Python"
print("Python" in text) # True
numbers = {1, 2, 3, 4, 5}
print(3 in numbers) # True
user_dict = {"name": "张三", "age": 25}
print("name" in user_dict) # True(检查键)
对比Java:
// Java需要用contains()方法
List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子");
boolean hasApple = fruits.contains("苹果");
// 或者字符串的contains()
String text = "Hello Python";
boolean hasPython = text.contains("Python");
实用场景:
# 验证输入
command = input("请输入命令: ")
if command not in ["start", "stop", "restart"]:
print("无效命令")
# 过滤数据
valid_ids = {1, 2, 3, 4, 5}
if user_id in valid_ids:
process_user(user_id)
# 字符串检查
email = "user@example.com"
if "@" not in email:
print("邮箱格式错误")
# 3.6、身份运算符(Python独有)
用于判断两个变量是否引用同一个对象(不是值相等,是对象相等)。
# is - 判断是否是同一个对象
a = [1, 2, 3]
b = a # b指向同一个列表对象
c = [1, 2, 3] # c是新的列表对象
print(a is b) # True - 指向同一对象
print(a is c) # False - 不同对象
print(a == c) # True - 但值相等
# is not
print(a is not c) # True
何时使用is?
# ✅ 检查None(推荐用is)
result = some_function()
if result is None:
print("没有返回值")
# ✅ 检查单例对象
if obj is True: # 检查是否是True对象
pass
# ❌ 不要用is比较值
x = 1000
y = 1000
if x is y: # 可能是False!(小整数有缓存,大整数没有)
print("相等")
# ✅ 应该用==比较值
if x == y: # 正确
print("相等")
小整数缓存机制:
# Python缓存了-5到256的整数
a = 10
b = 10
print(a is b) # True - 指向同一个缓存对象
# 大整数不缓存
a = 1000
b = 1000
print(a is b) # False(通常,取决于实现)
print(a == b) # True - 但值相等
# 3.7、运算符优先级
从高到低排列:
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 1 | ** | 幂运算 |
| 2 | ~, +x, -x | 按位取反、正号、负号 |
| 3 | *, /, //, % | 乘、除、整除、取模 |
| 4 | +, - | 加、减 |
| 5 | <<, >> | 位移 |
| 6 | & | 按位与 |
| 7 | ^ | 按位异或 |
| 8 | \| | 按位或 |
| 9 | ==, !=, >, <, >=, <=, is, is not, in, not in | 比较运算 |
| 10 | not | 逻辑非 |
| 11 | and | 逻辑与 |
| 12 | or | 逻辑或 |
实例:
# 复杂表达式
result = 2 + 3 * 4 ** 2 # 先幂运算(16),再乘法(48),最后加法(50)
print(result) # 50
# 建议:复杂表达式加括号提高可读性
result = 2 + (3 * (4 ** 2)) # 更清晰
# 逻辑运算
x = 5
result = x > 3 and x < 10 or x == 0 # (x > 3 and x < 10) or (x == 0)
print(result) # True
三元运算符(Python版本):
# Python的三元表达式
age = 25
status = "成年" if age >= 18 else "未成年"
# 对比Java
// String status = age >= 18 ? "成年" : "未成年";
# 可以嵌套(但不推荐,影响可读性)
score = 85
grade = "优秀" if score >= 90 else "良好" if score >= 80 else "及格" if score >= 60 else "不及格"
海象运算符 := (Python 3.8+)
在表达式中赋值:
# 传统写法
n = len(some_list)
if n > 10:
print(f"列表很长,有{n}个元素")
# 使用海象运算符
if (n := len(some_list)) > 10:
print(f"列表很长,有{n}个元素")
# 在while循环中特别有用
while (line := file.readline()) != "":
process(line)
小结对比
| 特性 | Java | Python |
|---|---|---|
| 除法 | /返回整数 | /总是返回浮点数 |
| 整除 | 用/ | 专门的// |
| 幂运算 | Math.pow() | **运算符 |
| 逻辑运算 | &&, \|\|, ! | and, or, not |
| 成员检查 | contains() | in, not in |
| 身份检查 | == | is, is not |
| 三元运算 | a ? b : c | b if a else c |
| 链式比较 | 不支持 | a < b < c |
# 4、控制流
Python的控制流语法比Java简洁,用缩进代替花括号,强制写出更清晰的代码结构。
# 4.1、if-elif-else条件语句
基本语法
age = 25
# 简单if语句
if age >= 18:
print("成年人")
# if-else
if age >= 18:
print("成年人")
else:
print("未成年")
# if-elif-else(相当于Java的if-else if-else)
if age < 18:
print("少年")
elif age < 60:
print("成年")
else:
print("老年")
对比Java:
// Java需要花括号
if (age >= 18) {
System.out.println("成年人");
} else if (age < 60) {
System.out.println("成年");
} else {
System.out.println("老年");
}
Python的优势:
# 链式比较
score = 85
if 90 <= score <= 100:
grade = "A"
elif 80 <= score < 90:
grade = "B"
elif 70 <= score < 80:
grade = "C"
else:
grade = "D"
# 成员运算符让条件更简洁
command = "start"
if command in ["start", "run", "begin"]:
print("启动程序")
elif command in ["stop", "end", "quit"]:
print("停止程序")
else:
print("未知命令")
利用Python的"真值"特性:
# ✅ Pythonic写法
name = input("请输入姓名: ")
if name: # 空字符串为False
print(f"欢迎, {name}")
else:
print("姓名不能为空")
# ✅ 检查列表是否为空
users = get_users()
if users: # 空列表为False
for user in users:
print(user)
else:
print("没有用户")
# 对比Java
// if (name != null && !name.isEmpty()) { ... }
// if (users != null && !users.isEmpty()) { ... }
# 4.2、while循环
基本语法
# 简单while循环
count = 0
while count < 5:
print(f"第{count + 1}次循环")
count += 1 # Python没有++运算符
# 无限循环
while True:
user_input = input("输入'quit'退出: ")
if user_input == "quit":
break
print(f"你输入了: {user_input}")
对比Java:
// Java
int count = 0;
while (count < 5) {
System.out.println("第" + (count + 1) + "次循环");
count++; // Java有++运算符
}
while-else(Python独有)
# while正常结束时执行else
search_list = [1, 3, 5, 7, 9]
target = 6
while search_list:
item = search_list.pop(0)
if item == target:
print(f"找到了: {target}")
break
else:
print(f"没找到: {target}") # 只有while正常结束才执行
# 如果用break退出,else不会执行
# 4.3、for循环与迭代器
这是Python最强大的特性之一!
遍历序列
# 遍历列表
fruits = ["苹果", "香蕉", "橙子"]
for fruit in fruits:
print(fruit)
# 遍历字符串
for char in "Python":
print(char)
# 遍历字典
user = {"name": "张三", "age": 25, "city": "北京"}
# 遍历键
for key in user:
print(key)
# 遍历值
for value in user.values():
print(value)
# 遍历键值对
for key, value in user.items():
print(f"{key}: {value}")
对比Java:
// Java需要更多代码
List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子");
// 传统for循环
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
// 增强for循环
for (String fruit : fruits) {
System.out.println(fruit);
}
enumerate() - 同时获取索引和值
fruits = ["苹果", "香蕉", "橙子"]
# 同时获取索引和值
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# 从指定索引开始
for index, fruit in enumerate(fruits, start=1):
print(f"第{index}个: {fruit}")
zip() - 并行遍历多个序列
names = ["张三", "李四", "王五"]
ages = [25, 30, 35]
cities = ["北京", "上海", "广州"]
# 并行遍历
for name, age, city in zip(names, ages, cities):
print(f"{name}, {age}岁, 来自{city}")
# 结果:
# 张三, 25岁, 来自北京
# 李四, 30岁, 来自上海
# 王五, 35岁, 来自广州
# 4.4、range()函数
专门用于生成数字序列,在for循环中特别有用。
# range(stop) - 从0开始
for i in range(5):
print(i) # 0, 1, 2, 3, 4
# range(start, stop) - 指定开始和结束
for i in range(2, 8):
print(i) # 2, 3, 4, 5, 6, 7
# range(start, stop, step) - 指定步长
for i in range(0, 10, 2):
print(i) # 0, 2, 4, 6, 8
# 反向
for i in range(10, 0, -1):
print(i) # 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
# 生成列表(但通常不需要)
numbers = list(range(5)) # [0, 1, 2, 3, 4]
实用场景:
# 传统的C风格循环
data = ["a", "b", "c", "d"]
for i in range(len(data)):
print(f"索引{i}: {data[i]}")
# 但更Pythonic的方式是用enumerate
for i, item in enumerate(data):
print(f"索引{i}: {item}")
# 创建嵌套列表(二维数组)
matrix = []
for i in range(3):
row = []
for j in range(4):
row.append(i * 4 + j)
matrix.append(row)
# 4.5、break、continue、pass
break - 跳出循环
# 在for循环中使用
for i in range(10):
if i == 5:
break
print(i) # 0, 1, 2, 3, 4
# 在while循环中使用
count = 0
while True:
if count >= 3:
break
print(f"计数: {count}")
count += 1
# 只跳出最内层循环
for i in range(3):
for j in range(3):
if j == 1:
break # 只跳出内层循环
print(f"i={i}, j={j}")
continue - 跳过本次循环
# 跳过偶数
for i in range(10):
if i % 2 == 0:
continue
print(i) # 1, 3, 5, 7, 9
# 处理文件,跳过空行
lines = ["第一行", "", "第三行", "", "第五行"]
for line in lines:
if not line: # 空字符串为False
continue
print(f"处理: {line}")
pass - 占位符
# 在开发中作为占位符
def future_function():
pass # 暂时不实现,避免语法错误
# 条件分支占位
score = 85
if score >= 90:
pass # 暂时不处理优秀情况
elif score >= 60:
print("及格")
else:
print("不及格")
# 类定义占位
class MyClass:
pass # 暂时空的类
# 异常处理占位
try:
risky_operation()
except SpecificError:
pass # 忽略特定错误
# 4.6、循环中的else子句
Python独有的特性,很多人不知道但很有用!
for-else
# else在循环正常结束时执行
numbers = [2, 4, 6, 8]
for num in numbers:
if num % 2 != 0: # 查找奇数
print(f"找到奇数: {num}")
break
else:
print("所有数字都是偶数") # 循环正常结束执行
# 搜索场景
users = ["张三", "李四", "王五"]
target = "赵六"
for user in users:
if user == target:
print(f"找到用户: {user}")
break
else:
print(f"用户 {target} 不存在") # 没找到时执行
while-else
# 密码验证(最多3次机会)
attempts = 0
max_attempts = 3
while attempts < max_attempts:
password = input("请输入密码: ")
if password == "123456":
print("密码正确,登录成功!")
break
attempts += 1
print(f"密码错误,还有{max_attempts - attempts}次机会")
else:
print("密码错误次数过多,账户锁定!") # 循环正常结束执行
# 4.7、嵌套循环与性能优化
嵌套循环
# 九九乘法表
for i in range(1, 10):
for j in range(1, i + 1):
print(f"{j}×{i}={i*j}", end="\t")
print() # 换行
# 查找重复元素
numbers = [1, 2, 3, 4, 2, 5, 3]
duplicates = []
for i in range(len(numbers)):
for j in range(i + 1, len(numbers)):
if numbers[i] == numbers[j] and numbers[i] not in duplicates:
duplicates.append(numbers[i])
print(f"重复元素: {duplicates}")
性能优化技巧
# ❌ 低效:每次都调用len()
data = list(range(1000))
for i in range(len(data)):
process(data[i])
# ✅ 高效:直接迭代
for item in data:
process(item)
# ❌ 低效:在循环中进行复杂计算
for i in range(1000):
result = expensive_calculation() # 每次都计算
process(i, result)
# ✅ 高效:提前计算
result = expensive_calculation() # 只计算一次
for i in range(1000):
process(i, result)
# ❌ 低效:字符串拼接
result = ""
for i in range(1000):
result += str(i)
# ✅ 高效:使用join
result = "".join(str(i) for i in range(1000))
# 4.8、实用的控制流技巧
Pythonic的代码提示
# 提示:这里展示的简洁写法将在第6章"数据结构"中详细讲解
# 包括列表推导式、字典推导式等Python特有语法
# 传统循环 vs Pythonic写法
# 传统方式
squares = []
for x in range(10):
squares.append(x ** 2)
# Pythonic方式(见第6章详解)
squares = [x ** 2 for x in range(10)]
多个条件的优雅处理
# 使用元组
def get_season(month):
if month in (12, 1, 2):
return "冬季"
elif month in (3, 4, 5):
return "春季"
elif month in (6, 7, 8):
return "夏季"
elif month in (9, 10, 11):
return "秋季"
else:
return "无效月份"
# 使用字典(更高效)
SEASON_MAP = {
1: "冬季", 2: "冬季", 3: "春季", 4: "春季",
5: "春季", 6: "夏季", 7: "夏季", 8: "夏季",
9: "秋季", 10: "秋季", 11: "秋季", 12: "冬季"
}
def get_season_fast(month):
return SEASON_MAP.get(month, "无效月份")
小结对比表
| 特性 | Java | Python |
|---|---|---|
| 代码块 | 花括号{} | 缩进 |
| else if | else if | elif |
| 循环后清理 | finally块 | else子句 |
| 遍历容器 | 增强for循环 | for item in container |
| 同时获取索引 | 手动计数 | enumerate() |
| 并行遍历 | 复杂 | zip() |
| 数字序列 | 手动创建 | range() |
| 占位符 | 空语句; | pass |
Python的控制流设计哲学是"简洁即是美",用更少的代码实现更清晰的逻辑!
# 5、数据结构与推导式
Python的内置数据结构非常强大,比Java的集合框架更简洁易用。特别值得一提的是Python的推导式语法,它让集合操作变得极其优雅。理解这些概念是掌握Python的关键。
# 5.1、列表(list)
列表是Python最常用的数据结构,相当于Java的ArrayList,但功能更强大。
基本操作
# 创建列表
fruits = ["苹果", "香蕉", "橙子"]
numbers = [1, 2, 3, 4, 5]
mixed = ["Python", 3.14, True, None] # 可以存储不同类型
# 访问元素
print(fruits[0]) # 苹果
print(fruits[-1]) # 橙子(负索引:从右往左)
# 修改元素
fruits[1] = "葡萄"
print(fruits) # ['苹果', '葡萄', '橙子']
# 列表长度
print(len(fruits)) # 3
列表操作方法
fruits = ["苹果", "香蕉"]
# 添加元素
fruits.append("橙子") # 末尾添加
fruits.insert(1, "葡萄") # 指定位置插入
fruits.extend(["芒果", "草莓"]) # 批量添加
print(fruits) # ['苹果', '葡萄', '香蕉', '橙子', '芒果', '草莓']
# 删除元素
fruits.remove("香蕉") # 删除指定元素
last_fruit = fruits.pop() # 删除并返回最后一个
second_fruit = fruits.pop(1) # 删除并返回指定位置
del fruits[0] # 直接删除指定位置
# 查找元素
if "苹果" in fruits:
index = fruits.index("苹果") # 返回首次出现的索引
count = fruits.count("苹果") # 统计出现次数
# 排序
numbers = [3, 1, 4, 1, 5, 9]
numbers.sort() # 原地排序
print(numbers) # [1, 1, 3, 4, 5, 9]
sorted_nums = sorted([3, 1, 4]) # 返回新列表
numbers.reverse() # 原地反转
对比Java:
// Java需要更多代码
List<String> fruits = new ArrayList<>();
fruits.add("苹果");
fruits.add("香蕉");
fruits.remove("香蕉");
Collections.sort(fruits);
列表切片
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 基本切片
print(numbers[2:5]) # [2, 3, 4](不包含索引5)
print(numbers[:3]) # [0, 1, 2](前3个)
print(numbers[7:]) # [7, 8, 9](从索引7到末尾)
print(numbers[:]) # 完整复制
# 步长切片
print(numbers[::2]) # [0, 2, 4, 6, 8](每隔一个)
print(numbers[1::2]) # [1, 3, 5, 7, 9](奇数位置)
print(numbers[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0](反转)
# 切片赋值(很强大!)
numbers[2:5] = [20, 30, 40] # 替换一段
numbers[2:2] = [15, 25] # 插入
numbers[2:4] = [] # 删除
列表推导式(List Comprehension)
这是Python的杀手级特性!
# 传统方式
squares = []
for x in range(10):
squares.append(x ** 2)
# 列表推导式(一行搞定)
squares = [x ** 2 for x in range(10)]
# 带条件的推导式
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
# 复杂处理
words = ["hello", "world", "python"]
capitalized = [word.upper() for word in words if len(word) > 4]
# ['HELLO', 'WORLD', 'PYTHON']
# 嵌套推导式(慎用,影响可读性)
matrix = [[x * y for x in range(3)] for y in range(3)]
# [[0, 0, 0], [0, 1, 2], [0, 2, 4]]
# 5.2、元组(tuple)
元组是不可变的序列,相当于只读的列表。
基本特性
# 创建元组
point = (3, 4)
colors = ("红", "绿", "蓝")
single = (42,) # 单元素元组需要逗号
empty = () # 空元组
# 或者省略括号
point = 3, 4
colors = "红", "绿", "蓝"
# 访问元素(和列表一样)
print(point[0]) # 3
print(colors[-1]) # 蓝
# 不能修改
# point[0] = 5 # TypeError
元组解包(Tuple Unpacking)
point = (3, 4)
# 解包赋值
x, y = point
print(f"坐标:({x}, {y})")
# 函数返回多个值
def get_name_age():
return "张三", 25
name, age = get_name_age()
# 交换变量(Python独有)
a, b = 10, 20
a, b = b, a # 交换,无需中间变量
# 扩展解包(Python 3+)
numbers = (1, 2, 3, 4, 5)
first, *middle, last = numbers
print(first) # 1
print(middle) # [2, 3, 4]
print(last) # 5
元组的用途
# 1. 函数返回多个值
def divmod_custom(a, b):
return a // b, a % b
quotient, remainder = divmod_custom(10, 3)
# 2. 作为字典的键(必须不可变)
locations = {
(0, 0): "原点",
(1, 1): "对角线",
(3, 4): "某个点"
}
# 3. 配置和常量
DEFAULT_CONFIG = ("localhost", 8080, "utf-8")
HOST, PORT, ENCODING = DEFAULT_CONFIG
# a、命名元组(namedtuple)
普通元组通过索引访问元素,可读性较差。namedtuple(命名元组)让你可以通过名称访问,同时保持元组的不可变性和性能优势。
from collections import namedtuple
# 定义命名元组
Point = namedtuple('Point', ['x', 'y'])
# 创建实例
p1 = Point(3, 4)
p2 = Point(x=10, y=20)
# 通过名称访问(更清晰)
print(p1.x, p1.y) # 3 4
# 也支持索引访问(兼容普通元组)
print(p1[0], p1[1]) # 3 4
# 不可变
# p1.x = 5 # AttributeError
定义方式对比
# 方式1:列表形式
Point = namedtuple('Point', ['x', 'y'])
# 方式2:空格分隔字符串
Point = namedtuple('Point', 'x y')
# 方式3:逗号分隔字符串
Point = namedtuple('Point', 'x, y')
实际应用
# 1. 表示坐标点
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(f"坐标: ({p.x}, {p.y})")
# 2. 数据库查询结果
Student = namedtuple('Student', ['name', 'age', 'score'])
students = [
Student('张三', 20, 85),
Student('李四', 21, 92),
Student('王五', 19, 78)
]
for s in students:
print(f"{s.name}: {s.score}分")
# 3. 函数返回多个值(更清晰)
def get_user_info():
User = namedtuple('User', ['name', 'age', 'email'])
return User('张三', 25, 'zhang@example.com')
user = get_user_info()
print(user.name) # 张三
print(user.email) # zhang@example.com
常用方法
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
# 转换为字典
print(p._asdict())
# {'x': 3, 'y': 4}
# 替换字段(返回新对象)
p2 = p._replace(x=10)
print(p2) # Point(x=10, y=4)
print(p) # Point(x=3, y=4)(原对象不变)
# 字段名称
print(p._fields) # ('x', 'y')
# 从序列创建
data = [5, 6]
p3 = Point._make(data)
print(p3) # Point(x=5, y=6)
扩展命名元组
# 添加默认值(Python 3.7+)
Point = namedtuple('Point', ['x', 'y'], defaults=[0, 0])
p1 = Point() # Point(x=0, y=0)
p2 = Point(3) # Point(x=3, y=0)
p3 = Point(3, 4) # Point(x=3, y=4)
# 继承并添加方法
from collections import namedtuple
class Point(namedtuple('Point', ['x', 'y'])):
"""扩展的点类"""
__slots__ = () # 节省内存
def distance_from_origin(self):
"""计算到原点的距离"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __str__(self):
return f"({self.x}, {self.y})"
p = Point(3, 4)
print(p.distance_from_origin()) # 5.0
print(p) # (3, 4)
对比普通元组
# 普通元组:可读性差
person = ("张三", 25, "北京")
print(person[0]) # 张三(需要记住索引含义)
print(person[1]) # 25
# 命名元组:可读性强
Person = namedtuple('Person', ['name', 'age', 'city'])
person = Person("张三", 25, "北京")
print(person.name) # 张三(语义清晰)
print(person.age) # 25
优势与限制
优势:
- ✅ 可读性强(通过名称访问)
- ✅ 不可变(线程安全)
- ✅ 内存效率高(比字典节省内存)
- ✅ 可以作为字典键
- ✅ 兼容普通元组(支持索引和解包)
限制:
- ❌ 不可变(不能修改字段值)
- ❌ 不支持默认值(Python 3.7之前)
- ❌ 字段名在定义后不能修改
对比Java:
// Java需要定义完整的类
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
}
// 或使用Java 14+ Record(更接近namedtuple)
public record Point(int x, int y) {}
何时使用namedtuple
- ✅ 需要轻量级的不可变数据容器
- ✅ 表示固定字段的记录(如坐标、配置)
- ✅ 函数返回多个值时提高可读性
- ✅ 替代字典以节省内存
- ❌ 需要可变数据时(用dataclass或普通类)
- ❌ 需要复杂行为时(用普通类)
# 5.3、字典(dict)
字典是键值对映射,相当于Java的HashMap,但使用更简单。
基本操作
# 创建字典
user = {"name": "张三", "age": 25, "city": "北京"}
empty_dict = {}
scores = dict(math=95, english=87, physics=92)
# 访问和修改
print(user["name"]) # 张三
print(user.get("phone")) # None(键不存在时返回None)
print(user.get("phone", "未设置")) # 未设置(提供默认值)
user["age"] = 26 # 修改
user["phone"] = "13800138000" # 添加新键
# 删除
del user["city"] # 删除键值对
phone = user.pop("phone") # 删除并返回值
last_item = user.popitem() # 删除并返回最后一个键值对
字典方法
user = {"name": "张三", "age": 25, "city": "北京"}
# 获取所有键、值、键值对
print(user.keys()) # dict_keys(['name', 'age', 'city'])
print(user.values()) # dict_values(['张三', 25, '北京'])
print(user.items()) # dict_keys([('name', '张三'), ('age', 25), ('city', '北京')])
# 遍历字典
for key in user:
print(f"{key}: {user[key]}")
for key, value in user.items():
print(f"{key}: {value}")
# 合并字典
defaults = {"theme": "dark", "lang": "zh"}
user.update(defaults) # 合并到user中
# Python 3.9+ 合并运算符
# config = defaults | user
字典推导式
# 传统方式
squares = {}
for x in range(5):
squares[x] = x ** 2
# 字典推导式
squares = {x: x ** 2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# 带条件
even_squares = {x: x ** 2 for x in range(10) if x % 2 == 0}
# 处理现有数据
words = ["apple", "banana", "cherry"]
word_lengths = {word: len(word) for word in words}
# {'apple': 5, 'banana': 6, 'cherry': 6}
有序字典特性(Python 3.7+)
# Python 3.7+字典保持插入顺序
user = {"name": "张三", "age": 25}
user["city"] = "北京"
print(list(user.keys())) # ['name', 'age', 'city'](保持顺序)
# 在早期版本中,如需有序字典:
from collections import OrderedDict
ordered_dict = OrderedDict([("a", 1), ("b", 2)])
# 5.4、集合(set)
集合是无序且不重复的元素集合,类似Java的HashSet。
基本操作
# 创建集合
fruits = {"苹果", "香蕉", "橙子"}
numbers = set([1, 2, 3, 4, 5])
empty_set = set() # 注意:{}是空字典
# 添加和删除元素
fruits.add("葡萄")
fruits.remove("香蕉") # 不存在会报错
fruits.discard("芒果") # 不存在不报错
popped = fruits.pop() # 随机删除并返回
# 成员检查(很快!)
print("苹果" in fruits) # True
集合运算
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}
# 并集
print(set1 | set2) # {1, 2, 3, 4, 5, 6, 7, 8}
print(set1.union(set2))
# 交集
print(set1 & set2) # {4, 5}
print(set1.intersection(set2))
# 差集
print(set1 - set2) # {1, 2, 3}
print(set1.difference(set2))
# 对称差集(异或)
print(set1 ^ set2) # {1, 2, 3, 6, 7, 8}
print(set1.symmetric_difference(set2))
# 子集和超集
print({1, 2}.issubset(set1)) # True
print(set1.issuperset({1, 2})) # True
集合推导式
# 去重并处理
numbers = [1, 2, 2, 3, 3, 4, 5]
unique_squares = {x ** 2 for x in numbers}
# {1, 4, 9, 16, 25}
# 实际应用:去重
emails = ["user1@example.com", "user2@example.com", "user1@example.com"]
unique_emails = set(emails)
frozenset(不可变集合)
# 创建不可变集合
immutable_set = frozenset([1, 2, 3, 4])
# 可以作为字典的键
cache = {
frozenset([1, 2]): "结果1",
frozenset([3, 4]): "结果2"
}
# 不能修改
# immutable_set.add(5) # AttributeError
# 5.5、与Java集合框架对比
对比表
| Python | Java | 特点 |
|---|---|---|
list | ArrayList | 动态数组,可变 |
tuple | 无直接对应 | 不可变序列 |
dict | HashMap | 键值映射,Python 3.7+有序 |
set | HashSet | 无序不重复集合 |
frozenset | 无直接对应 | 不可变集合 |
语法对比
// Java - 创建和操作
List<String> list = new ArrayList<>();
list.add("item");
String item = list.get(0);
Map<String, Integer> map = new HashMap<>();
map.put("key", 42);
Integer value = map.get("key");
Set<String> set = new HashSet<>();
set.add("item");
boolean exists = set.contains("item");
# Python - 更简洁
list = ["item"]
list.append("item2")
item = list[0]
dict = {"key": 42}
dict["key2"] = 43
value = dict["key"]
set = {"item"}
set.add("item2")
exists = "item" in set
实用技巧
# 列表去重并保持顺序
def unique_list(items):
seen = set()
result = []
for item in items:
if item not in seen:
seen.add(item)
result.append(item)
return result
# 或者用dict(Python 3.7+)
def unique_list_dict(items):
# dict.fromkeys 快速创建一个新字典,键为items中的元素,值为None
return list(dict.fromkeys(items))
# 统计元素频率
from collections import Counter
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
frequency = Counter(words)
print(frequency) # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
# 默认值字典
from collections import defaultdict
# defaultdict 创建一个默认值为空列表的dict
groups = defaultdict(list)
for name, score in [("张三", 95), ("李四", 87), ("张三", 92)]:
groups[name].append(score)
print(dict(groups)) # {'张三': [95, 92], '李四': [87]}
选择指南
- list:需要有序、可变、允许重复的序列
- tuple:需要不可变的序列,如坐标、配置
- dict:需要键值映射,如配置、缓存、索引
- set:需要去重、快速成员检查、集合运算
- frozenset:需要不可变的集合,如字典的键
Python的数据结构设计精妙,掌握它们就能写出更Pythonic的代码!
# 6、函数定义与使用
Python的函数比Java的方法更灵活强大,支持多种参数传递方式,而且函数是"一等公民"。
# 6.1、函数定义语法
基本语法
# 简单函数
def greet(name):
return f"Hello, {name}!"
# 带类型注解的函数(推荐)
def add(a: int, b: int) -> int:
"""计算两个数的和"""
return a + b
# 无返回值的函数
def print_info(name: str, age: int) -> None:
print(f"姓名: {name}, 年龄: {age}")
# 文档字符串(Docstring)
def calculate_area(radius: float) -> float:
"""
计算圆的面积
Args:
radius: 圆的半径
Returns:
圆的面积
Raises:
ValueError: 当半径为负数时
"""
if radius < 0:
raise ValueError("半径不能为负数")
return 3.14159 * radius ** 2
对比Java:
// Java需要更多样板代码
public class MathUtils {
/**
* 计算两个数的和
*/
public static int add(int a, int b) {
return a + b;
}
public static void printInfo(String name, int age) {
System.out.println("姓名: " + name + ", 年龄: " + age);
}
}
# 6.2、参数传递
Python的参数传递机制非常灵活,支持多种方式。
位置参数
def introduce(name, age, city):
return f"我叫{name},{age}岁,来自{city}"
# 按位置传递
result = introduce("张三", 25, "北京")
关键字参数
# 可以按任意顺序传递
result = introduce(city="北京", name="张三", age=25)
# 混合使用(位置参数必须在前)
result = introduce("张三", age=25, city="北京")
默认参数
def greet(name, greeting="Hello", punctuation="!"):
return f"{greeting}, {name}{punctuation}"
print(greet("张三")) # Hello, 张三!
print(greet("张三", "你好")) # 你好, 张三!
print(greet("张三", punctuation="?")) # Hello, 张三?
# 注意:默认参数的陷阱。在 Python 中,函数的默认参数在函数定义时(def 时)被创建一次,而不是每次调用时重新创建。
# [] 这个列表对象在函数定义时就创建了,所有后续调用共享同一个列表对象。
def append_to_list(item, target_list=[]): # ❌ 危险!
target_list.append(item)
return target_list
# 正确的写法
def append_to_list(item, target_list=None): # ✅ 正确
if target_list is None:
target_list = []
target_list.append(item)
return target_list
**可变参数(*args 和 kwargs)
# *args - 可变位置参数
def sum_all(*numbers):
"""计算所有数字的和"""
return sum(numbers)
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all(10, 20)) # 30
# **kwargs - 可变关键字参数
def create_user(**kwargs):
"""创建用户信息"""
user = {}
for key, value in kwargs.items():
user[key] = value
return user
user = create_user(name="张三", age=25, city="北京", job="程序员")
print(user) # {'name': '张三', 'age': 25, 'city': '北京', 'job': '程序员'}
# 混合使用
def flexible_function(required, *args, **kwargs):
print(f"必需参数: {required}")
print(f"位置参数: {args}")
print(f"关键字参数: {kwargs}")
flexible_function("必须的", 1, 2, 3, name="张三", age=25)
参数解包
# 解包列表/元组
def calculate(a, b, c):
return a + b * c
numbers = [2, 3, 4]
result = calculate(*numbers) # 相当于 calculate(2, 3, 4)
# 解包字典
def greet(name, age, city):
return f"{name}, {age}岁, 来自{city}"
person = {"name": "张三", "age": 25, "city": "北京"}
message = greet(**person) # 相当于 greet(name="张三", age=25, city="北京")
# 6.3、返回值与多返回值
单个返回值
def square(x):
return x ** 2
def get_greeting():
return "Hello, World!"
# 没有明确return,返回None
def print_message(msg):
print(msg)
# 隐式返回 None
多返回值(元组解包)
def get_name_age():
return "张三", 25 # 实际返回元组
# 解包接收
name, age = get_name_age()
def divmod_custom(a, b):
return a // b, a % b # 商和余数
quotient, remainder = divmod_custom(17, 5)
print(f"商: {quotient}, 余数: {remainder}") # 商: 3, 余数: 2
# 复杂的多返回值
def analyze_data(data):
total = sum(data)
count = len(data)
average = total / count if count > 0 else 0
return total, count, average, min(data), max(data)
data = [1, 5, 3, 9, 2]
total, count, avg, min_val, max_val = analyze_data(data)
# 6.4、函数作为一等公民
Python中函数是对象,可以赋值、传递、存储。
函数赋值
def greet(name):
return f"Hello, {name}!"
# 函数赋值给变量
say_hello = greet
print(say_hello("张三")) # Hello, 张三!
# 函数存储在数据结构中
operations = {
"add": lambda x, y: x + y,
"subtract": lambda x, y: x - y,
"multiply": lambda x, y: x * y
}
result = operations["add"](5, 3) # 8
函数作为参数
def apply_operation(numbers, operation):
"""对数字列表应用操作"""
return [operation(x) for x in numbers]
def square(x):
return x ** 2
def cube(x):
return x ** 3
numbers = [1, 2, 3, 4, 5]
squares = apply_operation(numbers, square) # [1, 4, 9, 16, 25]
cubes = apply_operation(numbers, cube) # [1, 8, 27, 64, 125]
# 使用内置函数
data = ["hello", "WORLD", "Python"]
uppercase = list(map(str.upper, data)) # ['HELLO', 'WORLD', 'PYTHON']
lengths = list(map(len, data)) # [5, 5, 6]
函数返回函数
def create_multiplier(factor):
"""创建一个乘法函数"""
def multiplier(x):
return x * factor
return multiplier
double = create_multiplier(2)
triple = create_multiplier(3)
print(double(5)) # 10
print(triple(4)) # 12
# 装饰器的基础
def timer_decorator(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间: {end - start:.4f}秒")
return result
return wrapper
@timer_decorator
def slow_function():
import time
time.sleep(1)
return "完成"
# 6.5、lambda表达式
lambda是创建匿名函数的简洁方式。
基本语法
# 基本lambda
square = lambda x: x ** 2
print(square(5)) # 25
# 多参数lambda
add = lambda x, y: x + y
print(add(3, 4)) # 7
# 对比普通函数
def square_func(x):
return x ** 2
# 效果相同,但lambda更简洁
实际应用
# 排序
students = [("张三", 85), ("李四", 92), ("王五", 78)]
# 按成绩排序
by_score = sorted(students, key=lambda student: student[1])
print(by_score) # [('王五', 78), ('张三', 85), ('李四', 92)]
# 过滤
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# 映射
squares = list(map(lambda x: x ** 2, range(5)))
print(squares) # [0, 1, 4, 9, 16]
# 字典操作
users = [
{"name": "张三", "age": 25},
{"name": "李四", "age": 30},
{"name": "王五", "age": 22}
]
# 按年龄排序
sorted_users = sorted(users, key=lambda user: user["age"])
# 6.6、作用域与LEGB规则
Python使用LEGB规则解析变量名。
LEGB规则
- Local(局部作用域)
- Enclosing(嵌套函数的外层作用域)
- Global(全局作用域)
- Built-in(内置作用域)
x = "全局变量"
def outer():
x = "外层函数变量"
def inner():
x = "内层函数变量"
print(f"内层: {x}") # 内层函数变量
inner()
print(f"外层: {x}") # 外层函数变量
outer()
print(f"全局: {x}") # 全局变量
global和nonlocal关键字
count = 0 # 全局变量
def increment_global():
# 在函数内部读取或修改模块级别(全局)的变量
global count
count += 1
def create_counter():
count = 0 # 局部变量
def increment():
# 在嵌套函数中修改外层函数的局部变量(不是全局变量)
nonlocal count
count += 1
return count
return increment
# 使用
increment_global()
print(count) # 1
counter = create_counter()
print(counter()) # 1
print(counter()) # 2
# 6.7、闭包
闭包是函数和其周围状态的组合。
基本闭包
def create_adder(x):
def adder(y):
return x + y # 捕获外层变量x
return adder
add_5 = create_adder(5)
add_10 = create_adder(10)
print(add_5(3)) # 8
print(add_10(3)) # 13
# 检查闭包变量
print(add_5.__closure__) # (<cell at 0x...: int object at 0x...>,)
实际应用
# 计数器
def create_counter(start=0):
count = start
def counter():
nonlocal count
count += 1
return count
def reset():
nonlocal count
count = start
def get_count():
return count
# 返回多个函数
counter.reset = reset
counter.get_count = get_count
return counter
counter = create_counter(10)
print(counter()) # 11
print(counter()) # 12
print(counter.get_count()) # 12
counter.reset()
print(counter()) # 11
# 配置工厂
def create_formatter(prefix, suffix):
def formatter(text):
return f"{prefix}{text}{suffix}"
return formatter
html_formatter = create_formatter("<p>", "</p>")
markdown_formatter = create_formatter("**", "**")
print(html_formatter("Hello")) # <p>Hello</p>
print(markdown_formatter("Bold")) # **Bold**
装饰器简介
# 提示:装饰器是Python的重要特性,将在第8章"高级特性"中详细讲解
# 这里简要展示装饰器与闭包的关系
def timer_decorator(func):
"""简单的计时装饰器(第8章详解)"""
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间: {end - start:.4f}秒")
return result
return wrapper
# 使用装饰器
@timer_decorator
def slow_function():
import time
time.sleep(1)
return "完成"
与Java对比
| 特性 | Java | Python |
|---|---|---|
| 函数定义 | 必须在类中 | 可以独立存在 |
| 方法重载 | 支持(不同参数) | 不支持(用默认参数) |
| 可变参数 | ...语法 | *args, **kwargs |
| 函数对象 | 需要接口/lambda | 函数即对象 |
| 闭包 | lambda表达式 | 完整闭包支持 |
| 作用域 | 块级作用域 | 函数级作用域 |
Python的函数系统比Java更灵活,支持函数式编程范式,能写出更简洁优雅的代码!
# 7、模块与包
Python的模块系统比Java的包系统更简单直观,一个.py文件就是一个模块,文件夹就是包。
# 7.1、模块导入方式
import语句
# 导入整个模块
import math
print(math.pi) # 3.141592653589793
print(math.sqrt(16)) # 4.0
import os
print(os.getcwd()) # 获取当前工作目录
# 导入多个模块
import sys, json, re
# 导入时重命名
import datetime as dt
now = dt.datetime.now()
from...import语句
# 导入模块中的特定函数/类
from math import pi, sqrt, sin
print(pi) # 直接使用,无需前缀
print(sqrt(16)) # 4.0
# 导入多个
from os import getcwd, listdir, path
# 导入所有(不推荐)
from math import * # 可能造成命名冲突
# 导入时重命名
from datetime import datetime as dt
now = dt.now()
import别名
# 常见的别名约定
import numpy as np # 数据科学常用
import pandas as pd # 数据分析
import matplotlib.pyplot as plt # 绘图
# 自定义模块别名
import my_very_long_module_name as short_name
对比Java:
// Java的import
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
// 静态导入
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
# 7.2、模块搜索路径
Python按以下顺序搜索模块:
import sys
# 查看模块搜索路径
for path in sys.path:
print(path)
# 1. 当前脚本所在目录
# 2. PYTHONPATH环境变量指定的目录
# 3. Python标准库目录
# 4. site-packages目录(第三方包)
# 动态添加搜索路径
sys.path.append('/path/to/my/modules')
sys.path.insert(0, '/priority/path') # 优先搜索
查找模块位置
import math
print(math.__file__) # 显示模块文件位置
# 或者用inspect模块
import inspect
import requests
print(inspect.getfile(requests))
# 7.3、__name__变量与入口点
这是Python模块系统的重要概念。
# my_module.py
def hello():
print("Hello from my_module!")
def main():
print("这是主程序")
hello()
# 关键代码:模块的入口点
if __name__ == "__main__":
main()
工作原理
# 当直接运行脚本时
# python my_module.py
# __name__ 的值是 "__main__"
# 当被其他模块导入时
# import my_module
# __name__ 的值是 "my_module"
# 实际应用
def add(a, b):
return a + b
def multiply(a, b):
return a * b
# 测试代码,只在直接运行时执行
if __name__ == "__main__":
# 这里的代码只在直接运行时执行,导入时不执行
print("测试模块功能...")
print(f"2 + 3 = {add(2, 3)}")
print(f"4 * 5 = {multiply(4, 5)}")
对比Java:
// Java的main方法
public class MyClass {
public static void add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
// 程序入口点
System.out.println("2 + 3 = " + add(2, 3));
}
}
# 7.4、包的组织结构
包就是包含__init__.py文件的目录。
基本包结构
mypackage/
__init__.py # 包的初始化文件
module1.py # 模块1
module2.py # 模块2
subpackage/ # 子包
__init__.py
submodule.py
创建包
# mypackage/__init__.py
"""
我的Python包
"""
# 包级别的变量
__version__ = "1.0.0"
__author__ = "张三"
# 导入子模块,方便使用
from .module1 import function1
from .module2 import Class2
# 定义包的公开接口
__all__ = ["function1", "Class2", "helper_function"]
def helper_function():
return "这是包级别的函数"
# mypackage/module1.py
def function1():
return "来自模块1的函数"
def internal_function():
return "内部函数"
# mypackage/module2.py
class Class2:
def method(self):
return "来自类2的方法"
# 7.5、__init__.py文件的作用
__init__.py让目录变成Python包,并控制包的导入行为。
基本作用
# 空的__init__.py
# 仅仅让目录成为包
# 或者包含初始化代码
print("正在初始化mypackage...")
# 导入子模块
from .database import connect
from .utils import helper
# 设置包级别变量
VERSION = "2.1.0"
DEBUG = False
控制导入
# mypackage/__init__.py
# 只导出特定的函数/类
from .core import important_function
from .helpers import utility_class
# 控制 from mypackage import * 的行为
__all__ = [
"important_function",
"utility_class",
"VERSION"
]
VERSION = "1.0.0"
# 初始化代码
def _initialize():
print("包初始化完成")
_initialize()
使用包
# 导入包
import mypackage
print(mypackage.VERSION)
result = mypackage.function1()
# 导入包中的模块
from mypackage import module1
result = module1.function1()
# 导入特定功能
from mypackage.module2 import Class2
obj = Class2()
# 7.6、相对导入与绝对导入
绝对导入(推荐)
# 总是从顶级包开始
from mypackage.subpackage import submodule
from mypackage.module1 import function1
import mypackage.database.models
相对导入
# 在包内部使用相对导入
# mypackage/module1.py
# 导入同级模块
from . import module2 # 当前包的module2
from .module2 import Class2 # 当前包module2的Class2
# 导入上级包
from .. import parent_module # 上级包的模块
from ..utils import helper # 上级包utils的helper
# 导入子包
from .subpackage import submodule # 子包的模块
最佳实践
# ✅ 推荐:使用绝对导入
from myproject.database import models
from myproject.utils.helpers import format_date
# ✅ 包内相对导入也可以
from .database import models
from ..utils import helpers
# ❌ 避免:隐式相对导入(Python 3已移除)
# import models # 不清楚来源
# 7.7、实际项目结构示例
典型的Python项目结构
myproject/
README.md
requirements.txt # 依赖列表
# 注意:现代 Python 推荐使用 pyproject.toml(PEP 518/621)作为首选打包配置方式,但 setup.py 仍广泛使用,尤其在旧项目中
setup.py # 安装脚本
myproject/ # 主包
__init__.py
main.py # 程序入口
config.py # 配置
models/ # 数据模型
__init__.py
user.py
product.py
views/ # 视图层
__init__.py
api.py
web.py
utils/ # 工具函数
__init__.py
helpers.py
validators.py
tests/ # 测试代码
__init__.py
test_models.py
test_views.py
配置模块
# myproject/config.py
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-secret')
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite:///app.db')
DEBUG = os.environ.get('DEBUG', 'False').lower() == 'true'
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
# 根据环境选择配置
config = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
主程序入口
# myproject/main.py
from myproject.config import config
from myproject.models import User
from myproject.utils.helpers import format_date
def main():
cfg = config['default']()
if cfg.DEBUG:
print("运行在调试模式")
# 业务逻辑
user = User("张三", 25)
print(f"用户: {user.name}, 注册日期: {format_date(user.created_at)}")
if __name__ == "__main__":
main()
# 7.8、第三方包管理
使用pip安装包
# 安装单个包
pip install requests
# 安装特定版本
pip install django==4.2.0
# 从requirements.txt安装
pip install -r requirements.txt
# 安装开发版本
pip install git+https://github.com/user/repo.git
requirements.txt示例
# 生产依赖
Django>=4.1,<5.0
requests>=2.28.0
psycopg2-binary==2.9.3
# 开发依赖(通常放在dev-requirements.txt)
pytest>=7.0.0
black>=22.0.0
flake8>=5.0.0
虚拟环境中的包管理
# 创建虚拟环境
python -m venv myproject_env
# 激活环境
# Windows
myproject_env\Scripts\activate
# macOS/Linux
source myproject_env/bin/activate
# 安装包
pip install -r requirements.txt
# 导出当前环境的包列表
pip freeze > requirements.txt
# 退出环境
deactivate
与Java对比
| 特性 | Java | Python |
|---|---|---|
| 基本单位 | 类(必须在包中) | 模块(.py文件) |
| 包声明 | package com.example | 目录+__init__.py |
| 导入 | import com.example.Class | from package import module |
| 依赖管理 | Maven/Gradle | pip+requirements.txt |
| 项目结构 | 强制目录结构 | 灵活的目录结构 |
| 模块搜索 | CLASSPATH | sys.path |
实用技巧
# 动态导入模块
import importlib
module_name = "math"
math_module = importlib.import_module(module_name)
print(math_module.pi)
# 检查模块是否存在
try:
import optional_module
HAS_OPTIONAL = True
except ImportError:
HAS_OPTIONAL = False
# 条件导入
if HAS_OPTIONAL:
from optional_module import feature
# 延迟导入(在函数内部导入)
def process_data():
import pandas as pd # 只在需要时导入
return pd.DataFrame()
# 获取模块信息
import inspect
import requests
print(f"模块名: {requests.__name__}")
print(f"模块文件: {requests.__file__}")
print(f"模块文档: {requests.__doc__}")
Python的模块系统设计简洁优雅,比Java的包系统更容易理解和使用。掌握模块和包的概念,能帮你更好地组织代码和管理项目!
# 三、面向对象编程
# 1、类与对象
Python的面向对象编程比Java更灵活,没有那么多严格的规则,但同样强大。
# 1.1、类的定义
基本语法
# 简单的类定义
class Person:
"""这是一个人类"""
pass # 空类,占位符
# 带属性和方法的类
class Dog:
"""狗类"""
# 类属性(所有实例共享)
species = "Canis familiaris"
def bark(self):
"""实例方法"""
return "汪汪汪!"
# 创建对象(实例化)
dog = Dog()
print(dog.species) # Canis familiaris
print(dog.bark()) # 汪汪汪!
对比Java:
// Java需要更多样板代码
public class Dog {
// 类属性(静态)
public static final String SPECIES = "Canis familiaris";
// 实例方法
public String bark() {
return "汪汪汪!";
}
}
// 实例化
Dog dog = new Dog();
# 1.2、构造函数(__init__)
Python使用__init__方法初始化对象,类似Java的构造函数。
基本构造函数
class Person:
def __init__(self, name, age):
"""构造函数,创建对象时自动调用"""
self.name = name # 实例属性
self.age = age
def introduce(self):
return f"我叫{self.name},{self.age}岁"
# 创建对象
person = Person("张三", 25)
print(person.introduce()) # 我叫张三,25岁
带默认值的构造函数
class User:
def __init__(self, username, email, is_active=True, role="user"):
self.username = username
self.email = email
self.is_active = is_active
self.role = role
def __repr__(self):
return f"User(username='{self.username}', email='{self.email}')"
# 使用默认值
user1 = User("张三", "zhangsan@example.com")
# 覆盖默认值
user2 = User("李四", "lisi@example.com", is_active=False, role="admin")
对比Java:
// Java的构造函数
public class Person {
private String name;
private int age;
// 构造函数
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 需要手动写getter/setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
# 1.3、实例属性与类属性
实例属性
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner # 实例属性
self.balance = balance # 每个实例独立
def deposit(self, amount):
self.balance += amount
return self.balance
account1 = BankAccount("张三", 1000)
account2 = BankAccount("李四", 2000)
account1.deposit(500)
print(account1.balance) # 1500
print(account2.balance) # 2000(互不影响)
类属性
class BankAccount:
# 类属性(所有实例共享)
bank_name = "Python银行"
interest_rate = 0.03
account_count = 0
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
# 修改类属性(通过类名)
BankAccount.account_count += 1
@classmethod
def get_account_count(cls):
"""类方法,访问类属性"""
return cls.account_count
# 访问类属性
print(BankAccount.bank_name) # Python银行
# 实例也可以访问
account = BankAccount("张三", 1000)
print(account.bank_name) # Python银行
# 创建多个实例
account1 = BankAccount("张三")
account2 = BankAccount("李四")
print(BankAccount.get_account_count()) # 2
实例属性 vs 类属性
class Example:
class_var = "类属性" # 类属性
def __init__(self, value):
self.instance_var = value # 实例属性
# 访问类属性
print(Example.class_var) # 类属性
# 实例访问
obj1 = Example("实例1")
obj2 = Example("实例2")
print(obj1.instance_var) # 实例1
print(obj2.instance_var) # 实例2
# 修改类属性
Example.class_var = "新的类属性"
print(obj1.class_var) # 新的类属性(所有实例都变了)
print(obj2.class_var) # 新的类属性
# 注意:实例赋值会创建同名的实例属性,不会修改类属性
obj1.class_var = "实例属性"
print(obj1.class_var) # 实例属性(实例的)
print(obj2.class_var) # 新的类属性(类的)
print(Example.class_var) # 新的类属性(类的)
# 1.4、实例方法、类方法、静态方法
实例方法
class Calculator:
def __init__(self, name):
self.name = name
# 实例方法:第一个参数是self
def add(self, a, b):
return a + b
def greet(self):
return f"我是{self.name}计算器"
calc = Calculator("科学")
print(calc.add(2, 3)) # 5
print(calc.greet()) # 我是科学计算器
类方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
# 类方法:第一个参数是cls(类本身)
@classmethod
def from_string(cls, date_string):
"""工厂方法:从字符串创建日期"""
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day) # 返回类的实例
@classmethod
def today(cls):
"""工厂方法:创建今天的日期"""
import datetime
now = datetime.date.today()
return cls(now.year, now.month, now.day)
# 面向用户的可读字符串
def __str__(self):
return f"{self.year}-{self.month:02d}-{self.day:02d}"
# 使用类方法
date1 = Date.from_string("2024-10-26")
date2 = Date.today()
print(date1) # 2024-10-26
print(date2) # 当前日期
静态方法
class MathUtils:
# 静态方法:不需要self或cls
@staticmethod
def is_even(number):
"""检查是否是偶数"""
return number % 2 == 0
@staticmethod
def factorial(n):
"""计算阶乘"""
if n <= 1:
return 1
return n * MathUtils.factorial(n - 1)
# 使用静态方法(无需创建实例)
print(MathUtils.is_even(4)) # True
print(MathUtils.factorial(5)) # 120
# 也可以通过实例调用
utils = MathUtils()
print(utils.is_even(3)) # False
三种方法的对比
| 方法类型 | 第一个参数 | 装饰器 | 访问方式 | 用途 |
|---|---|---|---|---|
| 实例方法 | self | 无 | 实例调用 | 操作实例数据 |
| 类方法 | cls | @classmethod | 类/实例调用 | 工厂方法、操作类属性 |
| 静态方法 | 无 | @staticmethod | 类/实例调用 | 工具函数、不需要访问类/实例数据 |
对比Java:
public class Example {
private String name;
// 实例方法
public void instanceMethod() {
System.out.println(this.name);
}
// 静态方法
public static void staticMethod() {
System.out.println("静态方法");
}
}
# 1.5、私有属性与方法
Python没有真正的私有属性,使用命名约定来表示。
命名约定
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner # 公开属性
self._balance = balance # 单下划线:受保护(约定)
self.__pin = "1234" # 双下划线:私有(名称改写)
def get_balance(self):
"""公开方法访问私有数据"""
return self._balance
def _internal_method(self):
"""受保护方法(约定)"""
return "内部使用"
def __private_method(self):
"""私有方法"""
return "真正的私有"
def check_pin(self, pin):
return self.__pin == pin
account = BankAccount("张三", 1000)
# 公开属性
print(account.owner) # 张三
# 受保护属性(可以访问,但不推荐)
print(account._balance) # 1000
# 私有属性(名称改写为 _BankAccount__pin)
# print(account.__pin) # AttributeError
print(account._BankAccount__pin) # 1234(强制访问,不推荐)
# 通过公开方法访问
print(account.get_balance()) # 1000
print(account.check_pin("1234")) # True
最佳实践
class User:
def __init__(self, username, password):
self.username = username # 公开
self._email = None # 受保护(内部使用)
self.__password_hash = self._hash_password(password) # 私有
def _hash_password(self, password):
"""受保护方法:密码哈希"""
# 简化示例
return hash(password)
def verify_password(self, password):
"""公开方法:验证密码"""
return self.__password_hash == self._hash_password(password)
对比Java:
public class BankAccount {
public String owner; // 公开
protected double balance; // 受保护
private String pin; // 私有
// 私有方法
private void privateMethod() {
// ...
}
}
# 1.6、属性访问器(property装饰器)
使用@property创建getter和setter,让方法像属性一样访问。
基本用法
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
# getter
@property
def celsius(self):
"""摄氏温度"""
return self._celsius
# setter
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
# 只读属性(只有getter)
@property
def fahrenheit(self):
"""华氏温度(只读)"""
return self._celsius * 9/5 + 32
# 使用
temp = Temperature(25)
print(temp.celsius) # 25(调用getter)
print(temp.fahrenheit) # 77.0
temp.celsius = 30 # 调用setter
print(temp.celsius) # 30
# temp.fahrenheit = 100 # AttributeError(只读)
# temp.celsius = -300 # ValueError(验证失败)
实际应用
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
"""姓名(只读)"""
return self._name
@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):
"""是否成年(计算属性)"""
return self._age >= 18
person = Person("张三", 25)
print(person.name) # 张三
print(person.age) # 25
print(person.is_adult) # True
person.age = 30 # 使用setter
# person.name = "李四" # AttributeError(只读)
删除器(deleter)
class Resource:
def __init__(self, name):
self._name = name
self._loaded = False
@property
def name(self):
return self._name
@property
def data(self):
if not self._loaded:
print("加载数据...")
self._loaded = True
return f"数据: {self._name}"
@data.deleter
def data(self):
"""删除数据"""
print("清理数据...")
self._loaded = False
resource = Resource("文件.txt")
print(resource.data) # 加载数据... 数据: 文件.txt
print(resource.data) # 数据: 文件.txt(已加载)
del resource.data # 清理数据...
print(resource.data) # 加载数据... 数据: 文件.txt
对比Java:
public class Temperature {
private double celsius;
// getter
public double getCelsius() {
return celsius;
}
// setter
public void setCelsius(double value) {
if (value < -273.15) {
throw new IllegalArgumentException("温度不能低于绝对零度");
}
this.celsius = value;
}
// 计算属性
public double getFahrenheit() {
return celsius * 9/5 + 32;
}
}
// 使用
Temperature temp = new Temperature();
temp.setCelsius(25); // 必须调用方法
double c = temp.getCelsius(); // 必须调用方法
property的优势
# Python使用@property,像访问属性一样
person.age = 30 # 自然的语法
temp = person.age
# Java必须调用方法
person.setAge(30); // 方法调用
int temp = person.getAge();
# Python可以在不破坏接口的情况下添加逻辑
class Circle:
def __init__(self, radius):
self.radius = radius # 最初是普通属性
# 后来添加验证,无需修改调用代码
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("半径不能为负数")
self._radius = value
小结对比表
| 特性 | Java | Python |
|---|---|---|
| 类定义 | class Name { } | class Name: |
| 构造函数 | public Name() { } | def __init__(self): |
| 实例属性 | private Type field; | self.field = value |
| 类属性 | static Type field; | 类级别定义 |
| 私有成员 | private | __name(名称改写) |
| 受保护成员 | protected | _name(约定) |
| getter/setter | 手动编写 | @property |
| 类方法 | static | @classmethod |
| 静态方法 | static | @staticmethod |
Python的类设计更简洁,没有Java那么多访问修饰符,通过约定和装饰器实现类似功能!
# 2、继承与多态
Python的继承机制比Java更灵活,支持多继承,并有独特的方法解析顺序(MRO)。
# 2.1、单继承
基本语法
# 父类(基类)
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "发出声音"
def info(self):
return f"我是{self.name}"
# 子类(派生类)
class Dog(Animal):
def __init__(self, name, breed):
# 调用父类构造函数
super().__init__(name)
self.breed = breed
# 重写父类方法
def speak(self):
return "汪汪汪!"
# 新增方法
def fetch(self):
return f"{self.name}去捡球了"
# 使用
dog = Dog("旺财", "金毛")
print(dog.info()) # 我是旺财(继承自Animal)
print(dog.speak()) # 汪汪汪!(重写了)
print(dog.fetch()) # 旺财去捡球了(新方法)
对比Java:
// Java的继承
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public String speak() {
return "发出声音";
}
}
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
@Override
public String speak() {
return "汪汪汪!";
}
}
isinstance()和issubclass()
class Animal:
pass
class Dog(Animal):
pass
dog = Dog()
# isinstance: 检查实例类型
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True(子类实例也是父类实例)
print(isinstance(dog, object)) # True(所有类都继承自object)
# issubclass: 检查继承关系
print(issubclass(Dog, Animal)) # True
print(issubclass(Animal, Dog)) # False
print(issubclass(Dog, object)) # True
# 2.2、多继承与MRO(方法解析顺序)
Python支持多继承,一个类可以继承多个父类。
基本多继承
class Flyable:
def fly(self):
return "能飞"
class Swimmable:
def swim(self):
return "能游泳"
class Duck(Flyable, Swimmable):
def quack(self):
return "嘎嘎嘎"
# 使用
duck = Duck()
print(duck.fly()) # 能飞
print(duck.swim()) # 能游泳
print(duck.quack()) # 嘎嘎嘎
MRO(Method Resolution Order)
当多个父类有同名方法时,Python使用C3线性化算法确定调用顺序。
class A:
def method(self):
return "A的方法"
class B(A):
def method(self):
return "B的方法"
class C(A):
def method(self):
return "C的方法"
class D(B, C):
pass
# 查看MRO
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
d = D()
print(d.method()) # B的方法(按MRO顺序:D -> B -> C -> A)
菱形继承问题
典型的菱形继承结构:
Person
/ \
Student Teacher
\ /
TeachingAssistant
示例代码:
class Person:
def __init__(self, name, **kwargs):
super().__init__(**kwargs) # 为未来多继承留接口
self.name = name
print(f"Person.__init__({name})")
class Student(Person):
def __init__(self, student_id, **kwargs):
super().__init__(**kwargs)
self.student_id = student_id
print(f"Student.__init__({student_id})")
class Teacher(Person):
def __init__(self, subject, **kwargs):
super().__init__(**kwargs)
self.subject = subject
print(f"Teacher.__init__({subject})")
class TeachingAssistant(Student, Teacher):
def __init__(self, name, student_id, subject):
super().__init__(name=name, student_id=student_id, subject=subject)
# 创建实例
ta = TeachingAssistant("张三", "S001", "Python")
print("\nMRO:", TeachingAssistant.__mro__)
print(f"\nta.name={ta.name}, ta.student_id={ta.student_id}, ta.subject={ta.subject}")
# 输出
Teacher.__init__(Python)
Student.__init__(S001)
Person.__init__(张三)
MRO: (<class '__main__.TeachingAssistant'>, <class '__main__.Student'>, <class '__main__.Teacher'>, <class '__main__.Person'>, <class 'object'>)
ta.name=张三, ta.student_id=S001, ta.subject=Python
# 注意:虽然 MRO 顺序是 Student 在 Teacher 前,但因为 Student.__init__ 调用了 super(),而 super() 在 Student 中指向 Teacher(根据 MRO),所以实际初始化顺序是 Teacher → Student → Person(从最右父类向左回溯)。
对比Java:
// Java不支持多继承,使用接口替代
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
public class Duck implements Flyable, Swimmable {
public void fly() {
System.out.println("能飞");
}
public void swim() {
System.out.println("能游泳");
}
}
# 2.3、super()函数
super()用于调用父类的方法,在多继承中特别有用。
基本用法
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Square(Rectangle):
def __init__(self, side):
# 调用父类构造函数
super().__init__(side, side)
def perimeter(self):
return 4 * self.width
square = Square(5)
print(square.area()) # 25
print(square.perimeter()) # 20
多继承中的super()
class Base:
def __init__(self):
print("Base初始化")
class A(Base):
def __init__(self):
print("A初始化开始")
super().__init__()
print("A初始化结束")
class B(Base):
def __init__(self):
print("B初始化开始")
super().__init__()
print("B初始化结束")
class C(A, B):
def __init__(self):
print("C初始化开始")
super().__init__() # 按MRO调用:C -> A -> B -> Base
print("C初始化结束")
c = C()
# 输出:
# C初始化开始
# A初始化开始
# B初始化开始
# Base初始化
# B初始化结束
# A初始化结束
# C初始化结束
显式调用父类方法
class Animal:
def speak(self):
return "发出声音"
class Dog(Animal):
def speak(self):
# 调用父类方法
parent_sound = super().speak()
return f"{parent_sound} -> 汪汪汪!"
def speak_like_parent(self):
# 也可以直接通过类名调用
return Animal.speak(self)
dog = Dog()
print(dog.speak()) # 发出声音 -> 汪汪汪!
print(dog.speak_like_parent()) # 发出声音
# 2.4、方法重写
子类可以重写(覆盖)父类的方法。
简单重写
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def get_bonus(self):
return self.salary * 0.1 # 基本奖金10%
class Manager(Employee):
def __init__(self, name, salary, team_size):
super().__init__(name, salary)
self.team_size = team_size
# 重写get_bonus方法
def get_bonus(self):
base_bonus = super().get_bonus() # 获取基本奖金
team_bonus = self.team_size * 1000 # 团队奖金
return base_bonus + team_bonus
# 使用
employee = Employee("张三", 10000)
manager = Manager("李四", 15000, 5)
print(employee.get_bonus()) # 1000.0
print(manager.get_bonus()) # 6500.0(1500 + 5000)
重写特殊方法
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name},{self.age}岁"
def __repr__(self):
return f"Person('{self.name}', {self.age})"
class Student(Person):
def __init__(self, name, age, grade):
super().__init__(name, age)
self.grade = grade
# 重写__str__
def __str__(self):
parent_str = super().__str__()
return f"{parent_str},{self.grade}年级"
# 重写__repr__
def __repr__(self):
return f"Student('{self.name}', {self.age}, {self.grade})"
student = Student("张三", 18, 12)
print(str(student)) # 张三,18岁,12年级
print(repr(student)) # Student('张三', 18, 12)
# 2.5、多态性实现
Python的多态是"鸭子类型"(Duck Typing):如果对象行为像鸭子,就可以当鸭子用。
基于继承的多态
class Shape:
def area(self):
raise NotImplementedError("子类必须实现area方法")
def perimeter(self):
raise NotImplementedError("子类必须实现perimeter方法")
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# 多态函数
def print_shape_info(shape):
"""接受任何Shape子类"""
print(f"面积: {shape.area():.2f}")
print(f"周长: {shape.perimeter():.2f}")
# 使用
rectangle = Rectangle(5, 3)
circle = Circle(4)
print_shape_info(rectangle)
print_shape_info(circle)
鸭子类型(Duck Typing)
# 不需要继承关系,只要有相同的方法就行
class Dog:
def speak(self):
return "汪汪汪"
class Cat:
def speak(self):
return "喵喵喵"
class Duck:
def speak(self):
return "嘎嘎嘎"
# 多态函数(不检查类型)
def make_animal_speak(animal):
"""只要有speak方法就行"""
print(animal.speak())
# 使用
dog = Dog()
cat = Cat()
duck = Duck()
make_animal_speak(dog) # 汪汪汪
make_animal_speak(cat) # 喵喵喵
make_animal_speak(duck) # 嘎嘎嘎
# 甚至可以传入任何有speak方法的对象
class Robot:
def speak(self):
return "哔哔哔"
make_animal_speak(Robot()) # 哔哔哔
对比Java:
// Java需要明确的继承或接口关系
public interface Animal {
String speak();
}
public class Dog implements Animal {
public String speak() {
return "汪汪汪";
}
}
public void makeAnimalSpeak(Animal animal) {
// 必须是Animal类型
System.out.println(animal.speak());
}
# 2.6、抽象基类(ABC模块)
如果想在Python中强制子类实现某些方法,可以使用抽象基类。
基本用法
from abc import ABC, abstractmethod
# 抽象基类
class Animal(ABC):
def __init__(self, name):
self.name = name
@abstractmethod
def speak(self):
"""子类必须实现"""
pass
@abstractmethod
def move(self):
"""子类必须实现"""
pass
def info(self):
"""具体方法,子类可以直接使用"""
return f"我是{self.name}"
# 具体实现
class Dog(Animal):
def speak(self):
return "汪汪汪"
def move(self):
return "跑动"
# 使用
# animal = Animal("动物") # TypeError: 不能实例化抽象类
dog = Dog("旺财")
print(dog.speak()) # 汪汪汪
print(dog.move()) # 跑动
print(dog.info()) # 我是旺财
抽象属性
from abc import ABC, abstractmethod
class Shape(ABC):
@property
@abstractmethod
def area(self):
"""抽象属性"""
pass
@property
@abstractmethod
def perimeter(self):
"""抽象属性"""
pass
class Rectangle(Shape):
def __init__(self, width, height):
self._width = width
self._height = height
@property
def area(self):
return self._width * self._height
@property
def perimeter(self):
return 2 * (self._width + self._height)
rect = Rectangle(5, 3)
print(rect.area) # 15
print(rect.perimeter) # 16
实际应用
from abc import ABC, abstractmethod
class DataProcessor(ABC):
"""数据处理抽象基类"""
@abstractmethod
def load_data(self, source):
"""加载数据"""
pass
@abstractmethod
def process_data(self, data):
"""处理数据"""
pass
@abstractmethod
def save_data(self, data, destination):
"""保存数据"""
pass
def run(self, source, destination):
"""模板方法(具体实现)"""
print("开始处理...")
data = self.load_data(source)
processed = self.process_data(data)
self.save_data(processed, destination)
print("处理完成!")
class CSVProcessor(DataProcessor):
def load_data(self, source):
print(f"从CSV加载: {source}")
return ["数据1", "数据2", "数据3"]
def process_data(self, data):
print("处理CSV数据...")
return [item.upper() for item in data]
def save_data(self, data, destination):
print(f"保存到CSV: {destination}")
print(f"数据: {data}")
# 使用
processor = CSVProcessor()
processor.run("input.csv", "output.csv")
对比Java:
// Java的抽象类
public abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
// 抽象方法
public abstract String speak();
// 具体方法
public String info() {
return "我是" + name;
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public String speak() {
return "汪汪汪";
}
}
小结对比表
| 特性 | Java | Python |
|---|---|---|
| 单继承 | extends | 括号语法 (Parent) |
| 多继承 | 不支持(用接口) | 支持多继承 |
| 调用父类 | super.method() | super().method() |
| 方法重写 | @Override注解 | 直接重写(无需注解) |
| 抽象类 | abstract class | ABC+@abstractmethod |
| 多态 | 基于类型 | 鸭子类型 |
| MRO | 不适用 | C3线性化算法 |
# 3、特殊方法(魔术方法)
Python的特殊方法(也叫魔术方法)是以双下划线开头和结尾的方法,它们让你的类能够使用Python的内置语法和运算符。这是Python面向对象编程的精髓之一。
# 3.1、对象表示:__str__和__repr__
这两个方法控制对象如何被转换为字符串。
基本区别
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""用户友好的字符串表示(给人看的)"""
return f"{self.name},{self.age}岁"
def __repr__(self):
"""开发者友好的字符串表示(给程序员看的)"""
return f"Person('{self.name}', {self.age})"
person = Person("张三", 25)
# __str__: 给最终用户看的
print(str(person)) # 张三,25岁
print(person) # 张三,25岁(print会调用__str__)
# __repr__: 给开发者看的,应该是合法的Python表达式
print(repr(person)) # Person('张三', 25)
print([person]) # [Person('张三', 25)](容器会调用__repr__)
# 交互式环境中
>>> person
Person('张三', 25) # 显示__repr__
最佳实践
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
"""始终实现__repr__"""
return f"Point({self.x}, {self.y})"
def __str__(self):
"""可选实现__str__,如果不实现会使用__repr__"""
return f"({self.x}, {self.y})"
# 只实现__repr__的类
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
# 没有__str__,print时会使用__repr__
point = Point(3, 4)
vector = Vector(1, 2)
print(point) # (3, 4) - 使用__str__
print(vector) # Vector(1, 2) - 使用__repr__
print(repr(point)) # Point(3, 4)
对比Java:
// Java使用toString()
public class Person {
private String name;
private int age;
@Override
public String toString() {
return name + "," + age + "岁";
}
}
// 使用
Person person = new Person("张三", 25);
System.out.println(person); // 张三,25岁
# 3.2、比较方法
Python提供了丰富的比较运算符重载方法。
基本比较方法
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
"""相等比较 =="""
if not isinstance(other, Student):
return False
return self.score == other.score
def __lt__(self, other):
"""小于比较 <"""
if not isinstance(other, Student):
return NotImplemented
return self.score < other.score
def __le__(self, other):
"""小于等于 <="""
return self.score <= other.score
def __gt__(self, other):
"""大于 >"""
return self.score > other.score
def __ge__(self, other):
"""大于等于 >="""
return self.score >= other.score
def __ne__(self, other):
"""不等于 !="""
return not self.__eq__(other)
def __repr__(self):
return f"Student('{self.name}', {self.score})"
# 使用
student1 = Student("张三", 85)
student2 = Student("李四", 92)
student3 = Student("王五", 85)
print(student1 == student3) # True(成绩相同)
print(student1 < student2) # True
print(student1 <= student3) # True
print(student2 > student1) # True
# 可以用于排序
students = [student2, student1, student3]
sorted_students = sorted(students)
print(sorted_students) # [Student('张三', 85), Student('王五', 85), Student('李四', 92)]
使用functools简化
from functools import total_ordering
@total_ordering
class Student:
"""只需实现__eq__和一个比较方法,自动生成其他"""
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
if not isinstance(other, Student):
return NotImplemented
return self.score == other.score
def __lt__(self, other):
if not isinstance(other, Student):
return NotImplemented
return self.score < other.score
def __repr__(self):
return f"Student('{self.name}', {self.score})"
# 自动拥有所有比较方法
student1 = Student("张三", 85)
student2 = Student("李四", 92)
print(student1 <= student2) # True(自动生成)
print(student1 >= student2) # False(自动生成)
# 3.3、容器方法
让你的类像列表、字典一样使用。
基本容器协议
class MyList:
"""自定义列表类"""
def __init__(self, items=None):
self._items = items or []
def __len__(self):
"""len()函数调用"""
return len(self._items)
def __getitem__(self, index):
"""索引访问 obj[index]"""
return self._items[index]
def __setitem__(self, index, value):
"""索引赋值 obj[index] = value"""
self._items[index] = value
def __delitem__(self, index):
"""删除 del obj[index]"""
del self._items[index]
def __contains__(self, item):
"""成员检查 item in obj"""
return item in self._items
def __iter__(self):
"""迭代 for item in obj"""
return iter(self._items)
def __repr__(self):
return f"MyList({self._items})"
# 使用
my_list = MyList([1, 2, 3, 4, 5])
print(len(my_list)) # 5
print(my_list[0]) # 1
my_list[0] = 10
print(my_list[0]) # 10
print(3 in my_list) # True
for item in my_list:
print(item, end=" ") # 10 2 3 4 5
del my_list[0]
print(my_list) # MyList([2, 3, 4, 5])
字典式访问
class Configuration:
"""配置类,支持字典式访问"""
def __init__(self):
self._config = {}
def __getitem__(self, key):
"""config['key']"""
return self._config[key]
def __setitem__(self, key, value):
"""config['key'] = value"""
self._config[key] = value
def __delitem__(self, key):
"""del config['key']"""
del self._config[key]
def __contains__(self, key):
"""'key' in config"""
return key in self._config
def __len__(self):
return len(self._config)
def __repr__(self):
return f"Configuration({self._config})"
# 使用
config = Configuration()
config['host'] = 'localhost'
config['port'] = 8080
print(config['host']) # localhost
print('host' in config) # True
print(len(config)) # 2
切片支持
class SmartList:
def __init__(self, items):
self._items = list(items)
def __getitem__(self, key):
"""支持索引和切片"""
if isinstance(key, slice):
# 切片操作
return SmartList(self._items[key])
else:
# 单个索引
return self._items[key]
def __len__(self):
return len(self._items)
def __repr__(self):
return f"SmartList({self._items})"
# 使用
smart = SmartList([1, 2, 3, 4, 5])
print(smart[0]) # 1
print(smart[1:3]) # SmartList([2, 3])
print(smart[::2]) # SmartList([1, 3, 5])
# 3.4、可调用对象:__call__
让对象可以像函数一样调用。
基本用法
class Multiplier:
"""乘法器,像函数一样使用"""
def __init__(self, factor):
self.factor = factor
def __call__(self, x):
"""obj(x) 会调用这个方法"""
return x * self.factor
# 使用
double = Multiplier(2)
triple = Multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
# 检查是否可调用
print(callable(double)) # True
实际应用:缓存装饰器
class Memoize:
"""缓存函数结果"""
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args):
if args not in self.cache:
print(f"计算 {self.func.__name__}{args}")
self.cache[args] = self.func(*args)
else:
print(f"从缓存获取 {args}")
return self.cache[args]
@Memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 使用
print(fibonacci(5))
# 计算 fibonacci(5)
# 计算 fibonacci(4)
# ...
# 21
print(fibonacci(5)) # 从缓存获取 (5)
有状态的函数
class Counter:
"""计数器函数"""
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
counter = Counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
print(counter.count) # 3
# 3.5、上下文管理器简介
提示:上下文管理器的完整用法(包括
@contextmanager装饰器、ExitStack等)将在第8章"高级特性"中详细讲解。
基本概念
上下文管理器通过实现__enter__和__exit__方法,让对象能够配合with语句自动管理资源。
class SimpleFileManager:
"""简单文件管理器示例"""
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
print(f"打开文件: {self.filename}")
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_value, traceback):
if self.file:
print(f"关闭文件: {self.filename}")
self.file.close()
# 返回False表示不抑制异常
return False
# 使用with语句自动管理资源
with SimpleFileManager('test.txt', 'w') as f:
f.write('Hello, World!')
# 文件自动关闭
核心协议
__enter__(self):进入with块时调用,返回要赋值给as变量的对象__exit__(self, exc_type, exc_value, traceback):退出with块时调用,处理清理工作
# 4、特殊方法总结
下面是一个实用的特殊方法速查表:
| 方法 | 触发时机 | 示例 |
|---|---|---|
__str__ | print(obj) | 对象的可读字符串表示 |
__repr__ | repr(obj) | 对象的明确表示 |
__len__ | len(obj) | 返回对象长度 |
__getitem__ | obj[key] | 索引访问 |
__setitem__ | obj[key] = value | 索引赋值 |
__call__ | obj() | 对象可调用 |
__enter__ | with obj as f: | 进入上下文 |
__exit__ | 退出with块 | 退出上下文 |
对比Java:
// Java使用try-with-resources
public class FileManager implements AutoCloseable {
private String filename;
public FileManager(String filename) {
this.filename = filename;
}
@Override
public void close() {
System.out.println("关闭文件: " + filename);
}
}
// 使用
try (FileManager fm = new FileManager("test.txt")) {
// 使用资源
} // 自动关闭
# 4.1、运算符重载
让自定义类支持各种运算符。
算术运算符
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):
"""乘法 *"""
return Vector(self.x * scalar, self.y * scalar)
def __truediv__(self, scalar):
"""除法 /"""
return Vector(self.x / scalar, self.y / scalar)
def __abs__(self):
"""绝对值 abs()"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __neg__(self):
"""取负 -obj"""
return Vector(-self.x, -self.y)
def __eq__(self, other):
"""相等 =="""
return self.x == other.x and self.y == other.y
def __repr__(self):
return f"Vector({self.x}, {self.y})"
# 使用
v1 = Vector(2, 3)
v2 = Vector(1, 1)
print(v1 + v2) # Vector(3, 4)
print(v1 - v2) # Vector(1, 2)
print(v1 * 2) # Vector(4, 6)
print(v1 / 2) # Vector(1.0, 1.5)
print(abs(v1)) # 3.605551275463989
print(-v1) # Vector(-2, -3)
print(v1 == v2) # False
反向运算符
class Number:
def __init__(self, value):
self.value = value
def __add__(self, other):
"""obj + other"""
return Number(self.value + other)
def __radd__(self, other):
"""other + obj(反向加法)"""
return Number(other + self.value)
def __mul__(self, other):
"""obj * other"""
return Number(self.value * other)
def __rmul__(self, other):
"""other * obj(反向乘法)"""
return Number(other * self.value)
def __repr__(self):
return f"Number({self.value})"
# 使用
num = Number(5)
print(num + 3) # Number(8) - 调用__add__
print(3 + num) # Number(8) - 调用__radd__
print(num * 2) # Number(10) - 调用__mul__
print(2 * num) # Number(10) - 调用__rmul__
增强赋值运算符
class Counter:
def __init__(self, value=0):
self.value = value
def __iadd__(self, other):
"""+="""
self.value += other
return self
def __isub__(self, other):
"""-="""
self.value -= other
return self
def __imul__(self, other):
"""*="""
self.value *= other
return self
def __repr__(self):
return f"Counter({self.value})"
# 使用
counter = Counter(10)
counter += 5
print(counter) # Counter(15)
counter -= 3
print(counter) # Counter(12)
counter *= 2
print(counter) # Counter(24)
完整运算符列表
| 运算符 | 方法 | 说明 |
|---|---|---|
+ | __add__ | 加法 |
- | __sub__ | 减法 |
* | __mul__ | 乘法 |
/ | __truediv__ | 除法 |
// | __floordiv__ | 整除 |
% | __mod__ | 取模 |
** | __pow__ | 幂运算 |
& | __and__ | 按位与 |
\| | __or__ | 按位或 |
^ | __xor__ | 按位异或 |
<< | __lshift__ | 左移 |
>> | __rshift__ | 右移 |
-obj | __neg__ | 取负 |
+obj | __pos__ | 取正 |
~obj | __invert__ | 按位取反 |
实际应用:货币类
class Money:
"""货币类,支持运算"""
def __init__(self, amount, currency="CNY"):
self.amount = amount
self.currency = currency
def __add__(self, other):
if self.currency != other.currency:
raise ValueError("不同币种不能相加")
return Money(self.amount + other.amount, self.currency)
def __sub__(self, other):
if self.currency != other.currency:
raise ValueError("不同币种不能相减")
return Money(self.amount - other.amount, self.currency)
def __mul__(self, factor):
"""乘以数字"""
return Money(self.amount * factor, self.currency)
def __truediv__(self, divisor):
"""除以数字"""
return Money(self.amount / divisor, self.currency)
def __eq__(self, other):
return self.amount == other.amount and self.currency == other.currency
def __lt__(self, other):
if self.currency != other.currency:
raise ValueError("不同币种不能比较")
return self.amount < other.amount
def __repr__(self):
return f"{self.currency} {self.amount:.2f}"
# 使用
price1 = Money(100.50, "CNY")
price2 = Money(50.25, "CNY")
total = price1 + price2
print(total) # CNY 150.75
discount = price1 * 0.8
print(discount) # CNY 80.40
print(price1 > price2) # True
# 4.2、其他常用特殊方法
__hash__:可哈希对象
class Person:
def __init__(self, name, id_number):
self.name = name
self.id_number = id_number
def __eq__(self, other):
if not isinstance(other, Person):
return False
return self.id_number == other.id_number
def __hash__(self):
"""使对象可以作为字典键和集合元素"""
return hash(self.id_number)
def __repr__(self):
return f"Person('{self.name}', '{self.id_number}')"
# 使用
person1 = Person("张三", "123456")
person2 = Person("张三", "123456")
person3 = Person("李四", "789012")
# 可以作为字典键
people = {
person1: "数据1",
person3: "数据2"
}
# 可以放入集合
person_set = {person1, person2, person3}
print(len(person_set)) # 2(person1和person2被认为是同一个)
__bool__:真值测试
class Result:
def __init__(self, data, error=None):
self.data = data
self.error = error
def __bool__(self):
"""if result: 时调用"""
return self.error is None
def __repr__(self):
if self.error:
return f"Result(error={self.error})"
return f"Result(data={self.data})"
# 使用
success = Result({"user": "张三"})
failure = Result(None, "连接失败")
if success:
print("成功!") # 输出
if not failure:
print("失败!") # 输出
__format__:格式化输出
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __format__(self, format_spec):
"""format(obj, spec) 时调用"""
if format_spec == 'p':
return f"({self.x}, {self.y})"
elif format_spec == 'c':
return f"{self.x}+{self.y}i"
else:
return f"Point({self.x}, {self.y})"
# 使用
point = Point(3, 4)
print(f"{point}") # Point(3, 4)
print(f"{point:p}") # (3, 4)
print(f"{point:c}") # 3+4i
小结对比表
| 特殊方法 | 用途 | Java对应 |
|---|---|---|
__str__ | 字符串表示 | toString() |
__repr__ | 开发者表示 | 无直接对应 |
__eq__ | 相等比较 | equals() |
__hash__ | 哈希值 | hashCode() |
__len__ | 长度 | size() |
__getitem__ | 索引访问 | get() |
__call__ | 可调用 | 函数式接口 |
__enter__/__exit__ | 上下文管理 | AutoCloseable |
__add__ | 加法 | 无(不支持运算符重载) |
Python的特殊方法让你的类能够无缝融入Python的语法体系,写出更Pythonic的代码!
# 5、Python数据类
Python 3.7引入的dataclass装饰器大大简化了数据类的定义,类似于Java 14+的record类。
# 5.1、基本用法
不使用dataclass的传统写法
# 传统写法:需要大量样板代码
class Person:
def __init__(self, name, age, city):
self.name = name
self.age = age
self.city = city
def __repr__(self):
return f"Person(name={self.name!r}, age={self.age!r}, city={self.city!r})"
def __eq__(self, other):
if not isinstance(other, Person):
return NotImplemented
return (self.name, self.age, self.city) == (other.name, other.age, other.city)
使用dataclass
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
city: str
# 自动生成__init__、__repr__、__eq__等方法
person = Person("张三", 25, "北京")
print(person) # Person(name='张三', age=25, city='北京')
person2 = Person("张三", 25, "北京")
print(person == person2) # True
对比Java Record:
// Java 14+ Record
public record Person(String name, int age, String city) {}
// 使用
Person person = new Person("张三", 25, "北京");
System.out.println(person); // Person[name=张三, age=25, city=北京]
# 5.2、字段定义与默认值
默认值
from dataclasses import dataclass
@dataclass
class User:
username: str
email: str
is_active: bool = True # 默认值
role: str = "user"
login_count: int = 0
# 使用
user1 = User("张三", "zhang@example.com")
print(user1)
# User(username='张三', email='zhang@example.com', is_active=True, role='user', login_count=0)
user2 = User("李四", "li@example.com", is_active=False, role="admin")
print(user2)
# User(username='李四', email='li@example.com', is_active=False, role='admin', login_count=0)
默认工厂函数
from dataclasses import dataclass, field
from typing import List
from datetime import datetime
@dataclass
class BlogPost:
title: str
content: str
tags: List[str] = field(default_factory=list) # 使用工厂函数
created_at: datetime = field(default_factory=datetime.now)
view_count: int = 0
# 使用
post1 = BlogPost("Python教程", "这是内容")
post2 = BlogPost("Java教程", "另一个内容", tags=["Java", "编程"])
print(post1.tags) # []
print(post2.tags) # ['Java', '编程']
print(post1.created_at) # 当前时间
# 注意:不要这样写(错误)
# tags: List[str] = [] # ❌ 所有实例共享同一个列表!
字段元数据
from dataclasses import dataclass, field
@dataclass
class Product:
name: str
price: float
stock: int = field(default=0, metadata={"unit": "件"})
description: str = field(default="", repr=False) # 不在repr中显示
_internal_id: int = field(default=0, init=False, repr=False) # 不在__init__中
# 使用
product = Product("笔记本电脑", 5999.00, 10)
print(product)
# Product(name='笔记本电脑', price=5999.0, stock=10)
# 访问元数据
from dataclasses import fields
for f in fields(Product):
if f.metadata:
print(f"{f.name}: {f.metadata}")
# stock: {'unit': '件'}
# 5.3、自动生成的特殊方法
可配置的方法生成
from dataclasses import dataclass
# 完整配置
@dataclass(
init=True, # 生成__init__(默认True)
repr=True, # 生成__repr__(默认True)
eq=True, # 生成__eq__(默认True)
order=False, # 生成比较方法__lt__等(默认False)
frozen=False, # 不可变(默认False)
unsafe_hash=False # 生成__hash__(默认False)
)
class Point:
x: int
y: int
# 不可变数据类
@dataclass(frozen=True)
class ImmutablePoint:
x: int
y: int
point = ImmutablePoint(3, 4)
# point.x = 5 # FrozenInstanceError
# 可比较的数据类
@dataclass(order=True)
class Student:
name: str
score: int
students = [Student("张三", 85), Student("李四", 92), Student("王五", 78)]
sorted_students = sorted(students)
print([s.name for s in sorted_students]) # ['张三', '李四', '王五']
自定义__post_init__
from dataclasses import dataclass
@dataclass
class Rectangle:
width: float
height: float
area: float = None # 计算属性
def __post_init__(self):
"""在__init__之后自动调用"""
if self.area is None:
self.area = self.width * self.height
# 验证
if self.width <= 0 or self.height <= 0:
raise ValueError("宽度和高度必须大于0")
# 使用
rect = Rectangle(5, 3)
print(rect.area) # 15.0
# rect = Rectangle(-5, 3) # ValueError
init-only字段
from dataclasses import dataclass, field, InitVar
@dataclass
class User:
username: str
password_hash: str = field(init=False, repr=False)
password: InitVar[str] = None # 仅在__init__中使用
def __post_init__(self, password):
"""password只在初始化时存在"""
if password:
self.password_hash = hash(password) # 简化示例
# 使用
user = User("张三", password="secret123")
print(user) # User(username='张三')
# user.password # AttributeError(不存在)
print(user.password_hash) # 哈希值
# 5.4、继承与组合
数据类继承
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
@dataclass
class Employee(Person):
employee_id: str
department: str
# 使用
emp = Employee("张三", 30, "E001", "技术部")
print(emp)
# Employee(name='张三', age=30, employee_id='E001', department='技术部')
组合模式
from dataclasses import dataclass
from typing import List
@dataclass
class Address:
street: str
city: str
country: str = "中国"
@dataclass
class Contact:
email: str
phone: str
@dataclass
class Company:
name: str
address: Address
contacts: List[Contact]
employee_count: int = 0
# 使用
address = Address("中关村大街1号", "北京")
contacts = [
Contact("hr@company.com", "010-12345678"),
Contact("sales@company.com", "010-87654321")
]
company = Company("科技公司", address, contacts, 500)
print(company.address.city) # 北京
print(company.contacts[0].email) # hr@company.com
# 5.5、高级特性
字段转换
from dataclasses import dataclass, field, asdict, astuple
@dataclass
class Book:
title: str
author: str
price: float
tags: list = field(default_factory=list)
book = Book("Python编程", "作者", 59.9, ["编程", "Python"])
# 转换为字典
book_dict = asdict(book)
print(book_dict)
# {'title': 'Python编程', 'author': '作者', 'price': 59.9, 'tags': ['编程', 'Python']}
# 转换为元组
book_tuple = astuple(book)
print(book_tuple)
# ('Python编程', '作者', 59.9, ['编程', 'Python'])
替换字段值
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class Point:
x: int
y: int
point1 = Point(3, 4)
point2 = replace(point1, x=5) # 创建新对象
print(point1) # Point(x=3, y=4)
print(point2) # Point(x=5, y=4)
实际应用:配置类
from dataclasses import dataclass, field
from typing import Optional
import json
@dataclass
class DatabaseConfig:
host: str = "localhost"
port: int = 5432
database: str = "mydb"
username: str = "admin"
password: str = field(default="", repr=False) # 不显示密码
pool_size: int = 10
timeout: int = 30
ssl: bool = False
def to_json(self) -> str:
"""导出为JSON"""
from dataclasses import asdict
return json.dumps(asdict(self), indent=2)
@classmethod
def from_json(cls, json_str: str):
"""从JSON创建"""
data = json.loads(json_str)
return cls(**data)
# 使用
config = DatabaseConfig(
host="prod-db.example.com",
database="production",
username="prod_user",
password="secret",
ssl=True
)
print(config)
# DatabaseConfig(host='prod-db.example.com', port=5432, database='production',
# username='prod_user', pool_size=10, timeout=30, ssl=True)
# 序列化
json_str = config.to_json()
print(json_str)
# 反序列化
config2 = DatabaseConfig.from_json(json_str)
# 5.6、与普通类和namedtuple对比
对比表
| 特性 | 普通类 | namedtuple | dataclass |
|---|---|---|---|
| 定义简洁度 | ❌ 冗长 | ✅ 简洁 | ✅ 简洁 |
| 可变性 | ✅ 可变 | ❌ 不可变 | ✅ 可配置 |
| 类型注解 | ❌ 手动 | ❌ 无 | ✅ 内置 |
| 默认值 | ✅ 支持 | ❌ 不支持 | ✅ 支持 |
| 继承 | ✅ 支持 | ⚠️ 复杂 | ✅ 支持 |
| 方法 | ✅ 支持 | ✅ 支持 | ✅ 支持 |
| 性能 | ✅ 好 | ✅ 好 | ✅ 好 |
示例对比
# 1. 普通类(最灵活,最冗长)
class PersonClass:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person(name={self.name!r}, age={self.age!r})"
def __eq__(self, other):
if not isinstance(other, PersonClass):
return NotImplemented
return (self.name, self.age) == (other.name, other.age)
# 2. namedtuple(简洁,不可变)
from collections import namedtuple
PersonTuple = namedtuple('Person', ['name', 'age'])
# 3. dataclass(最佳平衡)
from dataclasses import dataclass
@dataclass
class PersonData:
name: str
age: int
# 使用对比
p1 = PersonClass("张三", 25)
p2 = PersonTuple("张三", 25)
p3 = PersonData("张三", 25)
# 可变性
p1.age = 26 # ✅
# p2.age = 26 # ❌ AttributeError
p3.age = 26 # ✅
# 类型提示
# PersonClass: 无类型信息
# PersonTuple: 无类型信息
# PersonData: 完整类型注解
小结对比表
| 特性 | Python dataclass | Java record |
|---|---|---|
| 引入版本 | Python 3.7 | Java 14 |
| 可变性 | 默认可变,可配置 | 不可变 |
| 默认值 | 支持 | 不支持 |
| 继承 | 支持 | 不支持(可实现接口) |
| 生成方法 | 可配置 | 自动生成全部 |
| 序列化 | 需手动 | 自动支持 |
Python的数据类比Java的record更灵活,但两者都大大减少了数据承载类的样板代码!
# 6、特殊内置变量
Python中有大量以双下划线开头和结尾的特殊内置变量,它们由解释器自动设置,提供了丰富的运行时元信息。理解这些变量对于深入掌握Python至关重要。
区别:特殊内置变量是变量(存储值),魔术方法是方法(定义行为)
# 6.1、模块级别的特殊变量
__name__:模块名称
# my_module.py
print(f"模块名: {__name__}")
def main():
print("执行主程序")
if __name__ == "__main__":
# 只有直接运行时才执行
main()
# 直接运行
$ python my_module.py
# 输出: 模块名: __main__
# 执行主程序
# 作为模块导入
>>> import my_module
# 输出: 模块名: my_module
# main()不会执行
对比Java:
// Java用main方法作为入口
public class MyClass {
public static void main(String[] args) {
System.out.println("执行主程序");
}
}
__file__:文件路径
# utils.py
import os
print(f"文件路径: {__file__}")
print(f"绝对路径: {os.path.abspath(__file__)}")
print(f"所在目录: {os.path.dirname(__file__)}")
# 输出:
# 文件路径: /path/to/utils.py
# 绝对路径: /path/to/utils.py
# 所在目录: /path/to
实际应用:
# 读取同目录下的配置文件
import os
import json
# 获取当前脚本所在目录
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.path.join(CURRENT_DIR, 'config.json')
with open(CONFIG_FILE) as f:
config = json.load(f)
__doc__:文档字符串
"""
这是模块的文档字符串
用于描述模块功能
"""
def greet(name):
"""返回问候语"""
return f"Hello, {name}"
class Person:
"""人类"""
def __init__(self, name):
"""初始化Person对象"""
self.name = name
# 访问文档
print(__doc__) # 模块文档
print(greet.__doc__) # 函数文档
print(Person.__doc__) # 类文档
print(Person.__init__.__doc__) # 方法文档
__package__:包名
# mypackage/submodule.py
print(f"包名: {__package__}") # mypackage
print(f"模块名: {__name__}") # mypackage.submodule
# 用于相对导入
from . import other_module # 同级模块
from .. import parent_module # 上级模块
其他模块级变量
# 查看所有模块级特殊变量
import sys
print(f"__cached__: {__cached__}") # .pyc文件路径
print(f"__spec__: {__spec__}") # 模块规范对象
print(f"__loader__: {__loader__}") # 模块加载器
# __builtins__: 内置函数和异常
print(type(__builtins__)) # <class 'module'>
# 6.2、类级别的特殊变量
__class__:对象的类
class Person:
def __init__(self, name):
self.name = name
person = Person("张三")
# 获取类
print(person.__class__) # <class '__main__.Person'>
print(type(person)) # <class '__main__.Person'>(等价)
# 判断类型
print(person.__class__.__name__) # Person
# 通过类创建新实例
another = person.__class__("李四")
print(another.name) # 李四
__dict__:属性字典
class User:
class_var = "类变量"
def __init__(self, username, age):
self.username = username
self.age = age
user = User("张三", 25)
# 实例属性字典
print(user.__dict__)
# {'username': '张三', 'age': 25}
# 类属性字典
print(User.__dict__)
# {..., 'class_var': '类变量', '__init__': <function>, ...}
# 动态添加属性
user.__dict__['email'] = 'zhang@example.com'
print(user.email) # zhang@example.com
# 查看所有属性
for key, value in user.__dict__.items():
print(f"{key}: {value}")
对比Java:
// Java使用反射访问字段
import java.lang.reflect.Field;
Field[] fields = user.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.getName() + ": " + field.get(user));
}
__module__:类所在模块
# mypackage/models.py
class User:
pass
user = User()
print(user.__class__.__module__) # mypackage.models
print(User.__module__) # mypackage.models
__bases__:直接父类
class Animal:
pass
class Mammal(Animal):
pass
class Dog(Mammal):
pass
# 查看直接父类
print(Dog.__bases__) # (<class '__main__.Mammal'>,)
print(Mammal.__bases__) # (<class '__main__.Animal'>,)
print(Animal.__bases__) # (<class 'object'>,)
# 多继承
class Flyable:
pass
class Bird(Animal, Flyable):
pass
print(Bird.__bases__) # (<class '__main__.Animal'>, <class '__main__.Flyable'>)
__mro__:方法解析顺序
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
# MRO(C3线性化)
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>,
# <class '__main__.A'>, <class 'object'>)
# 也可以用mro()方法
print(D.mro())
__qualname__:限定名称
class Outer:
class Inner:
def method(self):
pass
print(Outer.__name__) # Outer
print(Outer.__qualname__) # Outer
print(Outer.Inner.__name__) # Inner
print(Outer.Inner.__qualname__) # Outer.Inner
print(Outer.Inner.method.__name__) # method
print(Outer.Inner.method.__qualname__) # Outer.Inner.method
# 6.3、函数级别的特殊变量
函数属性
def greet(name: str, age: int = 18) -> str:
"""返回问候语"""
return f"Hello, {name}! You are {age} years old."
# 函数名称
print(greet.__name__) # greet
# 文档字符串
print(greet.__doc__) # 返回问候语
# 所在模块
print(greet.__module__) # __main__
# 类型注解
print(greet.__annotations__)
# {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}
# 默认参数值
print(greet.__defaults__) # (18,)
# 代码对象
print(greet.__code__) # <code object greet at ...>
print(greet.__code__.co_varnames) # ('name', 'age')
print(greet.__code__.co_argcount) # 2
闭包变量
def outer(x):
y = 10
def inner(z):
return x + y + z
return inner
closure = outer(5)
# 闭包变量
print(closure.__closure__)
# (<cell at 0x...: int object at 0x...>, <cell at 0x...: int object at 0x...>)
# 查看闭包捕获的变量
for cell in closure.__closure__:
print(cell.cell_contents) # 5, 10
# 6.4、其他重要特殊变量
__debug__:调试模式
# 默认为True
print(__debug__) # True
# 使用-O优化模式运行时为False
# python -O script.py
# 用于条件调试代码
if __debug__:
print("调试信息:变量值为", x)
__all__:公开接口
# mymodule.py
__all__ = ['public_func', 'PublicClass']
def public_func():
"""公开函数"""
pass
def _internal_func():
"""内部函数"""
pass
class PublicClass:
"""公开类"""
pass
class _InternalClass:
"""内部类"""
pass
# 使用from import *时,只导入__all__中的内容
from mymodule import *
public_func() # ✅ 可用
# _internal_func() # ❌ NameError
__slots__:限制属性
class Point:
"""限制只能有x和y属性"""
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
point = Point(3, 4)
print(point.x, point.y) # 3 4
# 不能添加新属性
# point.z = 5 # AttributeError
# 没有__dict__(节省内存)
# print(point.__dict__) # AttributeError
优势:
- 节省内存(没有
__dict__) - 更快的属性访问
- 防止属性拼写错误
__annotations__:类型注解
class User:
name: str
age: int
email: str = "未设置"
def __init__(self, name: str, age: int):
self.name = name
self.age = age
# 查看类型注解
print(User.__annotations__)
# {'name': <class 'str'>, 'age': <class 'int'>, 'email': <class 'str'>}
# 函数的类型注解
def process(data: list[int]) -> int:
return sum(data)
print(process.__annotations__)
# {'data': list[int], 'return': <class 'int'>}
# 6.5、实用工具函数
dir():列出所有属性
class Person:
def __init__(self, name):
self.name = name
def greet(self):
pass
person = Person("张三")
# 列出所有属性和方法
print(dir(person))
# ['__class__', '__delattr__', ..., 'greet', 'name', ...]
# 只列出特殊变量/方法
special = [x for x in dir(person) if x.startswith('__') and x.endswith('__')]
print(special)
vars():返回__dict__
user = User("张三", 25)
# 等同于user.__dict__
print(vars(user))
# {'name': '张三', 'age': 25}
# 修改属性
vars(user)['age'] = 26
print(user.age) # 26
getattr()、setattr()、hasattr()
class Config:
debug = True
port = 8080
config = Config()
# 获取属性
debug = getattr(config, 'debug') # True
timeout = getattr(config, 'timeout', 30) # 30(默认值)
# 设置属性
setattr(config, 'host', 'localhost')
print(config.host) # localhost
# 检查属性
print(hasattr(config, 'debug')) # True
print(hasattr(config, 'timeout')) # False
# 动态操作
attr_name = 'port'
value = getattr(config, attr_name)
print(value) # 8080
# 6.6、实际应用案例
案例1:对象序列化
class Person:
def __init__(self, name, age, city):
self.name = name
self.age = age
self.city = city
def to_dict(self):
"""转换为字典"""
return self.__dict__.copy()
@classmethod
def from_dict(cls, data):
"""从字典创建对象"""
return cls(**data)
# 使用
person = Person("张三", 25, "北京")
data = person.to_dict()
print(data) # {'name': '张三', 'age': 25, 'city': '北京'}
# 反序列化
person2 = Person.from_dict(data)
print(person2.name) # 张三
案例2:插件系统
class PluginBase:
"""插件基类"""
pass
class Plugin1(PluginBase):
pass
class Plugin2(PluginBase):
pass
# 自动发现所有插件
def discover_plugins():
"""获取所有插件类"""
plugins = []
for cls in PluginBase.__subclasses__():
plugins.append(cls)
return plugins
print(discover_plugins())
# [<class '__main__.Plugin1'>, <class '__main__.Plugin2'>]
案例3:调试信息
def debug_info(obj):
"""打印对象的调试信息"""
print(f"类型: {obj.__class__.__name__}")
print(f"模块: {obj.__class__.__module__}")
print(f"MRO: {[c.__name__ for c in obj.__class__.__mro__]}")
print(f"属性: {obj.__dict__}")
class User:
def __init__(self, name):
self.name = name
user = User("张三")
debug_info(user)
# 类型: User
# 模块: __main__
# MRO: ['User', 'object']
# 属性: {'name': '张三'}
小结对比表
| 变量类型 | Python特殊变量 | Java对应 |
|---|---|---|
| 模块名 | __name__ | 无(用main方法) |
| 文件路径 | __file__ | 无直接对应 |
| 类信息 | __class__, __bases__ | getClass(), getSuperclass() |
| 属性字典 | __dict__ | 反射API |
| MRO | __mro__ | 无(单继承) |
| 文档 | __doc__ | JavaDoc注释 |
| 类型注解 | __annotations__ | 类型声明 |
记忆技巧:
- 模块相关:
__name__、__file__、__package__ - 类相关:
__class__、__bases__、__mro__、__module__ - 属性相关:
__dict__、__slots__、__annotations__ - 元信息:
__doc__、__qualname__、__all__
# 四、Python进阶特性
# 1、异常处理
Python的异常处理机制与Java类似,但语法更简洁,并提供了一些独特的特性。
# 1.1、try-except基本语法
基本用法
# 基本异常捕获
try:
number = int(input("请输入数字: "))
result = 10 / number
print(f"结果: {result}")
except ValueError:
print("输入的不是有效数字")
except ZeroDivisionError:
print("不能除以零")
对比Java:
// Java异常处理
try {
int number = Integer.parseInt(scanner.nextLine());
int result = 10 / number;
System.out.println("结果: " + result);
} catch (NumberFormatException e) {
System.out.println("输入的不是有效数字");
} catch (ArithmeticException e) {
System.out.println("不能除以零");
}
捕获多个异常
# 方式1:分别捕获
try:
data = json.loads(json_string)
except ValueError as e:
print(f"JSON解析错误: {e}")
except KeyError as e:
print(f"键不存在: {e}")
# 方式2:合并捕获(异常处理逻辑相同时)
try:
file = open("data.txt")
data = process(file)
except (FileNotFoundError, PermissionError) as e:
print(f"文件访问错误: {e}")
# 方式3:捕获所有异常(不推荐用于生产)
try:
risky_operation()
except Exception as e:
print(f"发生错误: {e}")
# 1.2、try-except-else-finally
Python的异常处理提供了else和finally子句。
else子句
try:
file = open("data.txt", "r")
data = file.read()
except FileNotFoundError:
print("文件不存在")
else:
# 只有try块成功时才执行
print(f"读取了{len(data)}个字符")
process_data(data)
finally:
# 无论如何都会执行
if 'file' in locals():
file.close()
print("文件已关闭")
完整示例
def divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("错误:除数不能为零")
return None
except TypeError:
print("错误:参数类型错误")
return None
else:
print(f"计算成功: {a} / {b} = {result}")
return result
finally:
print("除法操作完成")
# 使用
divide(10, 2)
# 输出:
# 计算成功: 10 / 2 = 5.0
# 除法操作完成
divide(10, 0)
# 输出:
# 错误:除数不能为零
# 除法操作完成
对比Java:
// Java没有else,但有finally
try {
result = a / b;
System.out.println("计算成功"); // 需要手动判断
} catch (ArithmeticException e) {
System.out.println("错误:除数不能为零");
} finally {
System.out.println("除法操作完成");
}
# 1.3、异常类型与继承体系
Python的异常都继承自BaseException。
异常层次结构
BaseException
├── SystemExit # 系统退出
├── KeyboardInterrupt # 用户中断(Ctrl+C)
├── GeneratorExit # 生成器退出
└── Exception # 常规异常的基类
├── StopIteration
├── ArithmeticError
│ ├── ZeroDivisionError
│ ├── OverflowError
│ └── FloatingPointError
├── AssertionError
├── AttributeError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── FileNotFoundError
│ ├── PermissionError
│ └── TimeoutError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── TypeError
├── ValueError
│ └── UnicodeError
└── Warning
常见异常
# ValueError:值错误
int("abc") # ValueError: invalid literal
# TypeError:类型错误
"string" + 123 # TypeError: can only concatenate str
# KeyError:字典键不存在
{"a": 1}["b"] # KeyError: 'b'
# IndexError:索引越界
[1, 2, 3][5] # IndexError: list index out of range
# AttributeError:属性不存在
"string".non_existent # AttributeError: 'str' object has no attribute
# FileNotFoundError:文件不存在
open("non_existent.txt") # FileNotFoundError
# ImportError:模块导入失败
import non_existent_module # ModuleNotFoundError
# 1.4、raise语句
抛出异常
def withdraw(amount, balance):
if amount <= 0:
raise ValueError("取款金额必须大于0")
if amount > balance:
raise ValueError(f"余额不足:需要{amount},当前{balance}")
return balance - amount
# 使用
try:
new_balance = withdraw(1000, 500)
except ValueError as e:
print(f"操作失败: {e}")
# 输出: 操作失败: 余额不足:需要1000,当前500
重新抛出异常
def process_data(data):
try:
result = risky_operation(data)
except ValueError as e:
print(f"警告: {e}")
raise # 重新抛出原异常
# 或者抛出新异常
def process_file(filename):
try:
# 提示:文件操作的详细内容请见第8章"文件操作与I/O"
with open(filename) as f:
return f.read()
except FileNotFoundError:
raise ValueError(f"配置文件{filename}不存在")
# 1.5、自定义异常
# 基本自定义异常
class ValidationError(Exception):
"""数据验证错误"""
pass
# 带额外信息的异常
class InsufficientFundsError(Exception):
"""余额不足异常"""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
self.shortage = amount - balance
super().__init__(f"余额不足:需要{amount},当前{balance},缺少{self.shortage}")
# 使用
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(balance, amount)
return balance - amount
try:
new_balance = withdraw(100, 150)
except InsufficientFundsError as e:
print(e)
print(f"缺少金额: {e.shortage}")
# 输出:
# 余额不足:需要150,当前100,缺少50
# 缺少金额: 50
异常基类最佳实践
class AppError(Exception):
"""应用程序基础异常"""
pass
class DatabaseError(AppError):
"""数据库相关错误"""
pass
class NetworkError(AppError):
"""网络相关错误"""
pass
class APIError(AppError):
"""API调用错误"""
def __init__(self, status_code, message):
self.status_code = status_code
self.message = message
super().__init__(f"API错误 {status_code}: {message}")
# 使用层次化捕获
try:
call_api()
except APIError as e:
print(f"API调用失败: {e}")
except NetworkError as e:
print(f"网络错误: {e}")
except AppError as e:
print(f"应用错误: {e}")
对比Java:
// Java自定义异常
public class InsufficientFundsException extends Exception {
private double balance;
private double amount;
public InsufficientFundsException(double balance, double amount) {
super("余额不足:需要" + amount + ",当前" + balance);
this.balance = balance;
this.amount = amount;
}
public double getShortage() {
return amount - balance;
}
}
# 1.6、异常链(Exception Chaining)
Python 3支持异常链,保留原始异常信息。
使用from关键字
def parse_config(filename):
try:
with open(filename) as f:
return json.load(f)
except FileNotFoundError as e:
raise ValueError(f"配置文件{filename}不存在") from e
except json.JSONDecodeError as e:
raise ValueError(f"配置文件格式错误") from e
try:
config = parse_config("config.json")
except ValueError as e:
print(f"错误: {e}")
print(f"原因: {e.__cause__}")
# 可以追溯到原始异常
隐式异常链
try:
try:
result = 10 / 0
except ZeroDivisionError:
# 在异常处理中又发生了异常
undefined_variable # NameError
except NameError as e:
print(f"当前异常: {e}")
print(f"之前的异常: {e.__context__}")
抑制异常链
try:
result = 10 / 0
except ZeroDivisionError:
# 使用from None抑制异常链
raise ValueError("计算错误") from None
# 1.7、实用异常处理模式
资源清理
# ❌ 不推荐
file = open("data.txt")
try:
data = file.read()
process(data)
finally:
file.close()
# ✅ 推荐:使用with语句
with open("data.txt") as file:
data = file.read()
process(data)
# 自动关闭文件
多个资源
# 管理多个资源
with open("input.txt") as infile, open("output.txt", "w") as outfile:
data = infile.read()
outfile.write(data.upper())
EAFP vs LBYL
Python推崇EAFP(Easier to Ask for Forgiveness than Permission,请求原谅比请求许可更容易)而非LBYL(Look Before You Leap,先检查再行动)。
# LBYL(Java风格)- 不推荐
if key in dictionary:
value = dictionary[key]
else:
value = default_value
# EAFP(Python风格)- 推荐
try:
value = dictionary[key]
except KeyError:
value = default_value
# 或者使用get方法
value = dictionary.get(key, default_value)
异常传播与日志
import logging
def process_user_data(user_id):
try:
user = fetch_user(user_id)
data = transform_data(user)
save_data(data)
except DatabaseError as e:
logging.error(f"数据库错误处理用户{user_id}: {e}")
raise # 重新抛出,让上层处理
except ValidationError as e:
logging.warning(f"验证失败用户{user_id}: {e}")
return None # 吞掉异常,返回默认值
except Exception as e:
logging.critical(f"未知错误处理用户{user_id}: {e}", exc_info=True)
raise # 致命错误,必须抛出
小结对比表
| 特性 | Python | Java |
|---|---|---|
| 基本语法 | try-except | try-catch |
| 多异常 | except (E1, E2) | 多个catch块或\| |
| else子句 | 支持 | 不支持 |
| finally | 支持 | 支持 |
| 异常链 | raise ... from | Throwable.initCause() |
| 检查异常 | 无 | 有(编译时检查) |
| 自定义异常 | 继承Exception | 继承Exception |
Python的异常处理更简洁,但缺少Java的检查异常机制。这让Python更灵活,但也需要开发者更自律地处理异常!
# 2、文件操作与I/O
Python的文件操作比Java简单直观得多,无需处理繁琐的流和缓冲区。
前置知识:异常处理已在第1章"异常处理"中介绍,本章会大量使用
with语句。
# 2.1、文件打开与关闭
基本操作
# 打开文件
file = open("data.txt", "r") # 读模式
content = file.read()
file.close() # 必须手动关闭
# ✅ 推荐:使用with语句自动关闭
with open("data.txt", "r") as file:
content = file.read()
# 自动关闭,即使发生异常也会关闭
文件模式
| 模式 | 说明 | Java对应 |
|---|---|---|
'r' | 只读(默认) | FileReader |
'w' | 写入(覆盖) | FileWriter |
'a' | 追加 | FileWriter(file, true) |
'x' | 独占创建(文件已存在则失败) | - |
'b' | 二进制模式 | FileInputStream |
't' | 文本模式(默认) | - |
'+' | 读写模式 | RandomAccessFile |
常用组合
# 文本文件
open("file.txt", "r") # 读
open("file.txt", "w") # 写(覆盖)
open("file.txt", "a") # 追加
open("file.txt", "r+") # 读写
# 二进制文件
open("image.png", "rb") # 读二进制
open("image.png", "wb") # 写二进制
对比Java:
// Java需要更多代码
try (BufferedReader reader = new BufferedReader(
new FileReader("data.txt"))) {
String content = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
# 2.2、文本文件读写
读取文件
# 方式1:一次性读取全部内容
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read()
print(content)
# 方式2:按行读取(返回列表)
with open("data.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
for line in lines:
print(line.strip()) # 去除换行符
# 方式3:逐行迭代(推荐,内存友好)
with open("data.txt", "r", encoding="utf-8") as f:
for line in f:
print(line.strip())
# 方式4:读取指定字节数
with open("data.txt", "r") as f:
chunk = f.read(100) # 读取前100个字符
写入文件
# 写入字符串
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello, World!\n")
f.write("第二行\n")
# 写入多行
lines = ["第一行\n", "第二行\n", "第三行\n"]
with open("output.txt", "w", encoding="utf-8") as f:
f.writelines(lines)
# 追加内容
with open("output.txt", "a", encoding="utf-8") as f:
f.write("追加的内容\n")
实用示例
# 复制文件
with open("source.txt", "r") as src, open("dest.txt", "w") as dst:
dst.write(src.read())
# 处理CSV
with open("data.csv", "r") as f:
for line in f:
fields = line.strip().split(",")
print(fields)
# 统计行数
with open("data.txt", "r") as f:
line_count = sum(1 for line in f)
print(f"总行数: {line_count}")
# 查找并替换
with open("input.txt", "r") as f:
content = f.read()
content = content.replace("old", "new")
with open("output.txt", "w") as f:
f.write(content)
# 2.3、二进制文件处理
读写二进制文件
# 读取二进制文件
with open("image.png", "rb") as f:
data = f.read()
print(f"文件大小: {len(data)} 字节")
# 写入二进制文件
with open("output.bin", "wb") as f:
f.write(b"\x00\x01\x02\x03")
# 复制二进制文件
with open("source.png", "rb") as src, open("dest.png", "wb") as dst:
dst.write(src.read())
# 分块读取大文件(内存友好)
with open("large_file.bin", "rb") as f:
while chunk := f.read(8192): # 每次读8KB
process(chunk)
使用struct模块处理二进制数据
import struct
# 写入结构化二进制数据
with open("data.bin", "wb") as f:
# 写入:1个整数,1个浮点数,5个字符
data = struct.pack("if5s", 42, 3.14, b"hello")
f.write(data)
# 读取结构化二进制数据
with open("data.bin", "rb") as f:
data = f.read()
num, pi, text = struct.unpack("if5s", data)
print(f"整数: {num}, 浮点: {pi}, 文本: {text.decode()}")
# 2.4、pathlib模块(现代路径操作)
pathlib是Python 3.4+推荐的路径操作方式,面向对象,比os.path更直观。
基本用法
from pathlib import Path
# 创建Path对象
file_path = Path("data/file.txt")
absolute_path = Path("/usr/local/bin/python")
# 获取当前目录
current_dir = Path.cwd()
print(current_dir)
# 获取用户主目录
home_dir = Path.home()
print(home_dir)
# 路径拼接
config_file = Path.home() / "config" / "settings.json"
# 等同于:Path.home().joinpath("config", "settings.json")
路径属性
file_path = Path("/home/user/documents/report.pdf")
print(file_path.name) # report.pdf(文件名)
print(file_path.stem) # report(文件名不含扩展名)
print(file_path.suffix) # .pdf(扩展名)
print(file_path.parent) # /home/user/documents(父目录)
print(file_path.parents[0]) # /home/user/documents
print(file_path.parents[1]) # /home/user
print(file_path.anchor) # /(根目录)
print(file_path.parts) # ('/', 'home', 'user', 'documents', 'report.pdf')
文件系统操作
from pathlib import Path
file_path = Path("data.txt")
# 检查存在性
if file_path.exists():
print("文件存在")
# 检查类型
if file_path.is_file():
print("是文件")
if file_path.is_dir():
print("是目录")
# 读写文件
file_path.write_text("Hello, World!", encoding="utf-8")
content = file_path.read_text(encoding="utf-8")
# 二进制读写
file_path.write_bytes(b"\x00\x01\x02")
data = file_path.read_bytes()
# 创建目录
Path("new_dir").mkdir(exist_ok=True) # 创建单层目录
Path("parent/child").mkdir(parents=True, exist_ok=True) # 创建多层
# 删除文件
file_path.unlink(missing_ok=True) # Python 3.8+
# 遍历目录
for item in Path(".").iterdir():
print(item)
# 模式匹配查找文件
for py_file in Path(".").glob("*.py"):
print(py_file)
for py_file in Path(".").rglob("*.py"): # 递归查找
print(py_file)
实用示例
from pathlib import Path
# 查找项目中所有Python文件
project_root = Path(".")
python_files = list(project_root.rglob("*.py"))
print(f"找到{len(python_files)}个Python文件")
# 统计代码行数
total_lines = 0
for py_file in python_files:
lines = py_file.read_text().count("\n")
total_lines += lines
print(f"总行数: {total_lines}")
# 安全地处理配置文件
config_file = Path.home() / ".myapp" / "config.json"
if not config_file.exists():
config_file.parent.mkdir(parents=True, exist_ok=True)
config_file.write_text('{"default": true}')
对比Java:
// Java使用Path和Files(Java 7+)
import java.nio.file.*;
Path filePath = Paths.get("/home/user/data.txt");
String content = Files.readString(filePath);
Files.writeString(filePath, "Hello");
// 遍历目录
try (var stream = Files.list(Paths.get("."))) {
stream.forEach(System.out::println);
}
# 2.5、标准输入输出
输入
# input():读取一行输入
name = input("请输入姓名: ")
print(f"你好, {name}!")
# 读取数字
age = int(input("请输入年龄: "))
# 读取多个值
x, y = map(int, input("输入两个数字(空格分隔): ").split())
输出
# print():标准输出
print("Hello, World!")
# 多个参数
print("姓名:", name, "年龄:", age)
# 自定义分隔符和结束符
print("a", "b", "c", sep="-") # a-b-c
print("loading", end="...") # loading...(不换行)
print(" done!") # loading... done!
# 写入文件
with open("output.txt", "w") as f:
print("写入文件", file=f)
格式化输出
name = "张三"
age = 25
score = 95.678
# f-string(推荐)
print(f"姓名: {name}, 年龄: {age}, 成绩: {score:.2f}")
# format方法
print("姓名: {}, 年龄: {}, 成绩: {:.2f}".format(name, age, score))
# %格式化(老式)
print("姓名: %s, 年龄: %d, 成绩: %.2f" % (name, age, score))
小结对比表
| 操作 | Python | Java |
|---|---|---|
| 打开文件 | open() | FileReader/BufferedReader |
| 自动关闭 | with语句 | try-with-resources |
| 读取全部 | read() | readAll()或循环读取 |
| 逐行读取 | for line in f | BufferedReader.readLine() |
| 路径操作 | pathlib.Path | java.nio.file.Path |
| 文件存在 | path.exists() | Files.exists() |
| 创建目录 | path.mkdir() | Files.createDirectories() |
Python的文件I/O操作比Java简洁优雅得多,pathlib模块提供了现代化的路径处理方式!
# 3、迭代器与生成器
迭代器和生成器是Python的强大特性,能够高效处理大数据集和实现惰性求值。
# 3.1、迭代器协议
Python的迭代器基于两个魔术方法:__iter__和__next__。
基本概念
# 可迭代对象(Iterable):实现了__iter__方法
numbers = [1, 2, 3, 4, 5]
iterator = iter(numbers) # 获取迭代器
# 迭代器(Iterator):实现了__iter__和__next__方法
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
# 迭代完毕后抛出StopIteration
# next(iterator) # 迭代完毕会抛出StopIteration
自定义迭代器
class Countdown:
"""倒计时迭代器"""
def __init__(self, start):
self.current = start
def __iter__(self):
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) # 5, 4, 3, 2, 1
实用迭代器示例
class FileReader:
"""逐行读取文件的迭代器"""
def __init__(self, filename):
self.file = open(filename, 'r')
def __iter__(self):
return self
def __next__(self):
line = self.file.readline()
if not line:
self.file.close()
raise StopIteration
return line.strip()
# 使用
for line in FileReader("data.txt"):
print(line)
对比Java:
// Java使用Iterator接口
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
# 3.2、生成器函数(yield)
生成器是创建迭代器的最简单方式,使用yield关键字。
基本用法
def countdown(n):
"""倒计时生成器"""
while n > 0:
yield n
n -= 1
# 使用
for num in countdown(5):
print(num) # 5, 4, 3, 2, 1
# 生成器是迭代器
gen = countdown(3)
print(next(gen)) # 3
print(next(gen)) # 2
print(next(gen)) # 1
# print(next(gen)) # StopIteration
工作原理
def simple_generator():
print("开始")
yield 1
print("继续")
yield 2
print("结束")
yield 3
gen = simple_generator()
print("创建生成器")
print(next(gen)) # 开始 -> 1
print(next(gen)) # 继续 -> 2
print(next(gen)) # 结束 -> 3
实用示例
# 1. 斐波那契数列
def fibonacci(n):
"""生成前n个斐波那契数"""
a, b = 0, 1
count = 0
while count < n:
yield a
a, b = b, a + b
count += 1
print(list(fibonacci(10)))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
# 2. 逐行读取大文件(内存友好)
def read_large_file(file_path):
"""逐行读取文件"""
with open(file_path) as f:
for line in f:
yield line.strip()
for line in read_large_file("huge_file.txt"):
process(line) # 一次只加载一行到内存
# 3. 批量处理数据
def batch(items, size):
"""将数据分批"""
batch = []
for item in items:
batch.append(item)
if len(batch) == size:
yield batch
batch = []
if batch: # 最后一批
yield batch
# 使用
data = range(100)
for batch in batch(data, 10):
print(f"处理批次: {batch}")
# 4. 无限序列
def infinite_sequence():
"""无限递增序列"""
num = 0
while True:
yield num
num += 1
gen = infinite_sequence()
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
# 3.3、生成器表达式
生成器表达式是列表推导式的生成器版本,使用圆括号。
注意:列表推导式和生成器表达式的完整用法,请参考第5章"数据结构与推导式"。
基本语法
# 列表推导式:立即计算,占用内存
squares_list = [x**2 for x in range(1000000)]
# 生成器表达式:惰性计算,节省内存
squares_gen = (x**2 for x in range(1000000))
# 使用
for square in squares_gen:
if square > 100:
break
print(square)
对比
import sys
# 列表推导式
list_comp = [x for x in range(10000)]
print(f"列表大小: {sys.getsizeof(list_comp)} bytes") # ~87616 bytes
# 生成器表达式
gen_expr = (x for x in range(10000))
print(f"生成器大小: {sys.getsizeof(gen_expr)} bytes") # ~112 bytes
实用示例
# 1. 过滤和转换
numbers = range(1, 11)
even_squares = (x**2 for x in numbers if x % 2 == 0)
print(list(even_squares)) # [4, 16, 36, 64, 100]
# 2. 链式处理
lines = (line.strip() for line in open("data.txt"))
non_empty = (line for line in lines if line)
uppercase = (line.upper() for line in non_empty)
for line in uppercase:
print(line)
# 3. 作为函数参数
sum_of_squares = sum(x**2 for x in range(10))
print(sum_of_squares) # 285
max_value = max((x**2 for x in range(10)))
print(max_value) # 81
# 4. 内存友好的数据处理
total = sum(int(line) for line in open("numbers.txt"))
# 3.4、itertools模块
itertools提供了高效的迭代器工具。
无限迭代器
from itertools import count, cycle, repeat
# count:无限计数
for i in count(10, 2): # 从10开始,步长2
if i > 20:
break
print(i) # 10, 12, 14, 16, 18, 20
# cycle:无限循环
counter = 0
for item in cycle(['A', 'B', 'C']):
if counter >= 5:
break
print(item, end=" ") # A B C A B
counter += 1
# repeat:重复元素
for item in repeat('Hello', 3):
print(item) # Hello(3次)
组合迭代器
from itertools import chain, zip_longest, product, combinations, permutations
# chain:连接多个迭代器
for item in chain([1, 2], [3, 4], [5, 6]):
print(item, end=" ") # 1 2 3 4 5 6
# zip_longest:配对(补齐)
for pair in zip_longest([1, 2, 3], ['a', 'b'], fillvalue='?'):
print(pair) # (1, 'a'), (2, 'b'), (3, '?')
# product:笛卡尔积
for pair in product([1, 2], ['a', 'b']):
print(pair) # (1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')
# combinations:组合
for combo in combinations([1, 2, 3, 4], 2):
print(combo) # (1,2), (1,3), (1,4), (2,3), (2,4), (3,4)
# permutations:排列
for perm in permutations([1, 2, 3], 2):
print(perm) # (1,2), (1,3), (2,1), (2,3), (3,1), (3,2)
过滤和分组
from itertools import filterfalse, dropwhile, takewhile, groupby
# filterfalse:过滤假值
data = [1, 0, 2, 0, 3]
non_zero = filterfalse(lambda x: x == 0, data)
print(list(non_zero)) # [1, 2, 3]
# dropwhile:丢弃直到条件为假
numbers = [1, 3, 5, 6, 7, 8, 9]
result = dropwhile(lambda x: x < 6, numbers)
print(list(result)) # [6, 7, 8, 9]
# takewhile:获取直到条件为假
result = takewhile(lambda x: x < 6, numbers)
print(list(result)) # [1, 3, 5]
# groupby:分组
data = [('A', 1), ('A', 2), ('B', 3), ('B', 4), ('A', 5)]
for key, group in groupby(data, lambda x: x[0]):
print(f"{key}: {list(group)}")
# A: [('A', 1), ('A', 2)]
# B: [('B', 3), ('B', 4)]
# A: [('A', 5)]
实用组合
from itertools import islice, tee, accumulate
# islice:切片迭代器
gen = (x**2 for x in range(100))
first_10 = list(islice(gen, 10))
print(first_10) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# tee:复制迭代器
gen1, gen2 = tee(range(5), 2)
print(list(gen1)) # [0, 1, 2, 3, 4]
print(list(gen2)) # [0, 1, 2, 3, 4]
# accumulate:累积
from operator import mul
numbers = [1, 2, 3, 4, 5]
print(list(accumulate(numbers))) # [1, 3, 6, 10, 15](累加)
print(list(accumulate(numbers, mul))) # [1, 2, 6, 24, 120](累乘)
# 3.5、惰性求值优势
生成器的惰性求值特性带来巨大优势。
内存效率
# ❌ 列表:立即计算,占用大量内存
def process_data_list():
data = [expensive_operation(x) for x in range(1000000)]
return sum(data)
# ✅ 生成器:惰性计算,内存占用小
def process_data_generator():
data = (expensive_operation(x) for x in range(1000000))
return sum(data)
无限序列
# 生成器可以表示无限序列
def primes():
"""无限素数生成器"""
yield 2
candidates = count(3, 2)
while True:
prime = next(candidates)
yield prime
candidates = (x for x in candidates if x % prime != 0)
# 取前10个素数
prime_gen = primes()
first_10_primes = [next(prime_gen) for _ in range(10)]
print(first_10_primes) # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
管道式处理
# 数据处理管道
def read_log(filename):
"""读取日志"""
with open(filename) as f:
for line in f:
yield line.strip()
def filter_errors(lines):
"""过滤错误"""
for line in lines:
if 'ERROR' in line:
yield line
def parse_timestamp(lines):
"""解析时间戳"""
for line in lines:
# 假设格式:[2024-01-01 10:00:00] ERROR: message
yield line.split(']')[0][1:]
# 管道组合
lines = read_log("app.log")
errors = filter_errors(lines)
timestamps = parse_timestamp(errors)
# 惰性执行,只在需要时处理
for ts in timestamps:
print(ts)
小结对比表
| 特性 | 列表 | 生成器 | Java Stream |
|---|---|---|---|
| 求值方式 | 立即 | 惰性 | 惰性 |
| 内存占用 | 大 | 小 | 小 |
| 可重复使用 | ✅ | ❌ | ❌ |
| 索引访问 | ✅ | ❌ | ❌ |
| 长度获取 | ✅ | ❌ | ❌ |
| 无限序列 | ❌ | ✅ | ✅ |
# 4、装饰器
装饰器是Python的强大特性,允许在不修改原函数/类的情况下增强其功能。这类似于Java的注解+AOP,但更灵活强大。
前置知识:装饰器基于闭包概念,闭包已在第6章"函数定义与使用"中介绍。
# 4.1、函数装饰器基础
基本概念
装饰器本质是一个接受函数作为参数并返回新函数的高阶函数。
# 最简单的装饰器
def my_decorator(func):
"""装饰器函数"""
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
# 使用装饰器(方式1:手动包装)
def say_hello():
print("Hello!")
say_hello = my_decorator(say_hello)
say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后
# 使用装饰器(方式2:@语法糖)
@my_decorator
def say_world():
print("World!")
say_world()
# 输出:
# 函数执行前
# World!
# 函数执行后
带参数的函数装饰
def my_decorator(func):
def wrapper(*args, **kwargs):
"""接受任意参数"""
print(f"调用 {func.__name__},参数: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"返回值: {result}")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
@my_decorator
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
# 使用
result = add(3, 5)
# 调用 add,参数: (3, 5), {}
# 返回值: 8
message = greet("张三", greeting="你好")
# 调用 greet,参数: ('张三',), {'greeting': '你好'}
# 返回值: 你好, 张三!
对比Java:
// Java需要使用注解+AOP或代理模式
@Around("@annotation(LogExecution)")
public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法执行前");
Object result = joinPoint.proceed();
System.out.println("方法执行后");
return result;
}
@LogExecution
public void sayHello() {
System.out.println("Hello!");
}
# 4.2、functools.wraps保留元信息
装饰器会改变函数的元信息,functools.wraps用于保留原函数的元数据。
from functools import wraps
# ❌ 不使用wraps
def bad_decorator(func):
def wrapper(*args, **kwargs):
"""这是wrapper的文档"""
return func(*args, **kwargs)
return wrapper
@bad_decorator
def my_function():
"""这是my_function的文档"""
pass
print(my_function.__name__) # wrapper(错误!)
print(my_function.__doc__) # 这是wrapper的文档(错误!)
# ✅ 使用wraps
def good_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""这是wrapper的文档"""
return func(*args, **kwargs)
return wrapper
@good_decorator
def my_function2():
"""这是my_function2的文档"""
pass
print(my_function2.__name__) # my_function2(正确!)
print(my_function2.__doc__) # 这是my_function2的文档(正确!)
标准装饰器模板
from functools import wraps
def my_decorator(func):
"""标准装饰器模板"""
@wraps(func)
def wrapper(*args, **kwargs):
# 执行前的逻辑
print(f"Before calling {func.__name__}")
# 调用原函数
result = func(*args, **kwargs)
# 执行后的逻辑
print(f"After calling {func.__name__}")
return result
return wrapper
# 4.3、带参数的装饰器
装饰器本身也可以接受参数。
from functools import wraps
def repeat(times):
"""重复执行装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i in range(times):
print(f"第{i+1}次执行:")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("张三")
# 输出:
# 第1次执行:
# Hello, 张三!
# 第2次执行:
# Hello, 张三!
# 第3次执行:
# Hello, 张三!
工作原理
# @repeat(3) 等价于:
# greet = repeat(3)(greet)
# 分步理解:
decorator = repeat(3) # 调用repeat(3),返回decorator函数
greet = decorator(greet) # 调用decorator(greet),返回wrapper
实用带参数装饰器示例
from functools import wraps
import time
def retry(max_attempts=3, delay=1):
"""重试装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise e
print(f"第{attempt + 1}次尝试失败,{delay}秒后重试...")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=5, delay=2)
def unstable_api_call():
"""不稳定的API调用"""
import random
if random.random() < 0.7:
raise ConnectionError("API调用失败")
return "成功"
# 使用
result = unstable_api_call()
# 4.4、类装饰器
类也可以作为装饰器,通过实现__call__方法。
from functools import wraps
class CountCalls:
"""统计函数调用次数"""
def __init__(self, func):
wraps(func)(self)
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 已被调用 1 次 -> Hello!
say_hello() # say_hello 已被调用 2 次 -> Hello!
say_hello() # say_hello 已被调用 3 次 -> Hello!
print(say_hello.count) # 3
带参数的类装饰器
from functools import wraps
class LogWith:
"""带日志级别的装饰器"""
def __init__(self, level="INFO"):
self.level = level
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{self.level}] 调用 {func.__name__}")
result = func(*args, **kwargs)
print(f"[{self.level}] {func.__name__} 返回 {result}")
return result
return wrapper
@LogWith(level="DEBUG")
def add(a, b):
return a + b
result = add(3, 5)
# [DEBUG] 调用 add
# [DEBUG] add 返回 8
# 4.5、装饰器叠加
多个装饰器可以叠加使用。
def make_bold(func):
@wraps(func)
def wrapper(*args, **kwargs):
return "<b>" + func(*args, **kwargs) + "</b>"
return wrapper
def make_italic(func):
@wraps(func)
def wrapper(*args, **kwargs):
return "<i>" + func(*args, **kwargs) + "</i>"
return wrapper
@make_bold
@make_italic
def say_hello():
return "Hello!"
print(say_hello()) # <b><i>Hello!</i></b>
# 等价于:
# say_hello = make_bold(make_italic(say_hello))
# 执行顺序:从下到上装饰,从上到下执行
顺序理解
def decorator1(func):
print(f"装饰器1 装饰 {func.__name__}")
@wraps(func)
def wrapper(*args, **kwargs):
print("装饰器1: 执行前")
result = func(*args, **kwargs)
print("装饰器1: 执行后")
return result
return wrapper
def decorator2(func):
print(f"装饰器2 装饰 {func.__name__}")
@wraps(func)
def wrapper(*args, **kwargs):
print("装饰器2: 执行前")
result = func(*args, **kwargs)
print("装饰器2: 执行后")
return result
return wrapper
@decorator1
@decorator2
def test():
print("执行test函数")
# 装饰阶段输出:
# 装饰器2 装饰 test
# 装饰器1 装饰 wrapper
test()
# 执行阶段输出:
# 装饰器1: 执行前
# 装饰器2: 执行前
# 执行test函数
# 装饰器2: 执行后
# 装饰器1: 执行后
# 4.6、常用内置装饰器
Python提供了一些常用的内置装饰器。
@property(属性装饰器)
详细内容:property装饰器的完整用法已在第3章"面向对象编程"中详细介绍。
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""半径(只读)"""
return self._radius
@property
def area(self):
"""面积(计算属性)"""
return 3.14159 * self._radius ** 2
@area.setter
def area(self, value):
"""通过面积反推半径"""
self._radius = (value / 3.14159) ** 0.5
circle = Circle(5)
print(circle.area) # 78.53975
circle.area = 100
print(circle.radius) # 5.641895835477563
@staticmethod(静态方法)
class MathUtils:
@staticmethod
def add(a, b):
"""静态方法,不需要访问类或实例"""
return a + b
@staticmethod
def is_even(n):
return n % 2 == 0
# 使用
print(MathUtils.add(3, 5)) # 8
print(MathUtils.is_even(4)) # True
@classmethod(类方法)
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_string):
"""工厂方法:从字符串创建"""
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day)
@classmethod
def today(cls):
"""工厂方法:创建今天的日期"""
import datetime
now = datetime.date.today()
return cls(now.year, now.month, now.day)
# 使用
date1 = Date.from_string("2024-10-26")
date2 = Date.today()
@cached_property(缓存属性)
from functools import cached_property
class DataProcessor:
def __init__(self, data):
self.data = data
@cached_property
def processed_data(self):
"""计算量大的属性,只计算一次"""
print("处理数据中...")
import time
time.sleep(2) # 模拟耗时操作
return [x * 2 for x in self.data]
processor = DataProcessor([1, 2, 3, 4, 5])
print(processor.processed_data) # 处理数据中... [2, 4, 6, 8, 10]
print(processor.processed_data) # [2, 4, 6, 8, 10](直接返回缓存)
# 4.7、装饰器实战案例
案例1:性能计时器
from functools import wraps
import time
def timer(func):
"""测量函数执行时间"""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行时间: {end - start:.4f}秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "完成"
result = slow_function()
# slow_function 执行时间: 1.0012秒
案例2:权限检查
from functools import wraps
def require_auth(func):
"""检查用户是否已登录"""
@wraps(func)
def wrapper(*args, **kwargs):
# 假设有一个全局的当前用户对象
if not hasattr(wrapper, 'current_user') or not wrapper.current_user:
raise PermissionError("需要登录")
return func(*args, **kwargs)
return wrapper
def require_role(role):
"""检查用户角色"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not hasattr(wrapper, 'current_user'):
raise PermissionError("需要登录")
if wrapper.current_user.get('role') != role:
raise PermissionError(f"需要{role}权限")
return func(*args, **kwargs)
return wrapper
return decorator
@require_auth
def view_profile():
return "个人资料"
@require_role('admin')
def delete_user(user_id):
return f"删除用户 {user_id}"
# 设置当前用户
view_profile.current_user = {"username": "张三", "role": "user"}
delete_user.current_user = {"username": "管理员", "role": "admin"}
print(view_profile()) # 个人资料
print(delete_user(123)) # 删除用户 123
案例3:缓存/记忆化
from functools import wraps
def memoize(func):
"""缓存函数结果"""
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
print(f"计算 {func.__name__}{args}")
cache[args] = func(*args)
else:
print(f"从缓存获取 {args}")
return cache[args]
# 添加清除缓存的方法
wrapper.cache = cache
wrapper.clear_cache = lambda: cache.clear()
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
# 计算 fibonacci(10)
# 计算 fibonacci(9)
# ...
# 55
print(fibonacci(10)) # 从缓存获取 (10)
print(fibonacci.cache) # 查看缓存内容
fibonacci.clear_cache() # 清除缓存
案例4:输入验证
from functools import wraps
def validate_types(**type_hints):
"""验证函数参数类型"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 获取函数签名
import inspect
sig = inspect.signature(func)
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()
# 验证类型
for param_name, expected_type in type_hints.items():
if param_name in bound.arguments:
value = bound.arguments[param_name]
if not isinstance(value, expected_type):
raise TypeError(
f"{param_name} 应为 {expected_type.__name__},"
f"实际为 {type(value).__name__}"
)
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(name=str, age=int, salary=float)
def create_employee(name, age, salary):
return f"{name}, {age}岁, 薪资{salary}"
# 正确调用
print(create_employee("张三", 25, 5000.0))
# 错误调用
try:
create_employee("李四", "30", 6000.0) # age类型错误
except TypeError as e:
print(e) # age 应为 int,实际为 str
案例5:日志记录
from functools import wraps
import logging
logging.basicConfig(level=logging.INFO)
def log_calls(log_args=True, log_result=True):
"""记录函数调用日志"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
logger = logging.getLogger(func.__module__)
# 记录调用
if log_args:
logger.info(f"调用 {func.__name__},参数: {args}, {kwargs}")
else:
logger.info(f"调用 {func.__name__}")
try:
result = func(*args, **kwargs)
# 记录结果
if log_result:
logger.info(f"{func.__name__} 返回: {result}")
return result
except Exception as e:
logger.error(f"{func.__name__} 抛出异常: {e}")
raise
return wrapper
return decorator
@log_calls(log_args=True, log_result=True)
def divide(a, b):
return a / b
result = divide(10, 2)
# INFO:__main__:调用 divide,参数: (10, 2), {}
# INFO:__main__:divide 返回: 5.0
try:
divide(10, 0)
except ZeroDivisionError:
pass
# INFO:__main__:调用 divide,参数: (10, 0), {}
# ERROR:__main__:divide 抛出异常: division by zero
案例6:单例模式
from functools import wraps
def singleton(cls):
"""单例装饰器"""
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class DatabaseConnection:
def __init__(self, host, port):
self.host = host
self.port = port
print(f"连接到数据库 {host}:{port}")
# 使用
db1 = DatabaseConnection("localhost", 5432)
# 连接到数据库 localhost:5432
db2 = DatabaseConnection("localhost", 5432)
# (不会打印,因为返回的是同一个实例)
print(db1 is db2) # True
小结对比表
| 特性 | Python装饰器 | Java注解+AOP |
|---|---|---|
| 语法 | @decorator | @Annotation |
| 灵活性 | 高(运行时修改行为) | 中(需要额外配置) |
| 嵌套 | 支持多层装饰 | 支持 |
| 带参数 | 支持 | 支持 |
| 类装饰 | 支持装饰类 | 主要用于方法 |
| 内省 | 容易(__wrapped__) | 需要反射 |
Python的装饰器比Java的注解更灵活强大,可以在运行时动态修改函数/类的行为,而且语法更简洁!
# 5、上下文管理器
上下文管理器让资源管理变得优雅安全,自动处理资源的获取和释放。这类似于Java的try-with-resources,但更灵活。
# 5.1、with语句原理
基本概念
with语句确保资源在使用后被正确清理,即使发生异常也能保证。
# 传统方式:需要手动管理
file = open("data.txt", "r")
try:
content = file.read()
process(content)
finally:
file.close() # 必须手动关闭
# with语句:自动管理
with open("data.txt", "r") as file:
content = file.read()
process(content)
# 自动关闭文件,即使发生异常
对比Java:
// Java try-with-resources(Java 7+)
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String content = reader.readLine();
process(content);
} // 自动关闭资源
上下文管理器协议
上下文管理器需要实现__enter__和__exit__方法:
class MyContextManager:
def __enter__(self):
"""进入with块时调用"""
print("进入上下文")
return self # 返回值赋给as后的变量
def __exit__(self, exc_type, exc_value, traceback):
"""退出with块时调用"""
print("退出上下文")
if exc_type is not None:
print(f"发生异常: {exc_type.__name__}: {exc_value}")
return False # False表示不抑制异常
# 使用
with MyContextManager() as cm:
print("执行代码")
# raise ValueError("测试异常")
# 输出:
# 进入上下文
# 执行代码
# 退出上下文
__exit__方法参数说明
def __exit__(self, exc_type, exc_value, traceback):
"""
exc_type: 异常类型(如果没有异常则为None)
exc_value: 异常实例(如果没有异常则为None)
traceback: 异常堆栈(如果没有异常则为None)
返回值:
- False或None: 异常继续传播
- True: 抑制异常(异常被吞掉)
"""
pass
# 5.2、自定义上下文管理器
文件操作管理器
class FileManager:
"""文件管理器"""
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
"""打开文件"""
print(f"打开文件: {self.filename}")
self.file = open(self.filename, self.mode)
return self.file
def __exit__(self, exc_type, exc_value, traceback):
"""关闭文件"""
if self.file:
print(f"关闭文件: {self.filename}")
self.file.close()
return False
# 使用
with FileManager("test.txt", "w") as f:
f.write("Hello, World!")
# 输出:
# 打开文件: test.txt
# 关闭文件: test.txt
数据库连接管理器
class DatabaseConnection:
"""数据库连接管理器"""
def __init__(self, host, database):
self.host = host
self.database = database
self.connection = None
def __enter__(self):
"""建立连接"""
print(f"连接数据库: {self.host}/{self.database}")
# 这里模拟连接
self.connection = f"Connection({self.host}, {self.database})"
return self.connection
def __exit__(self, exc_type, exc_value, traceback):
"""关闭连接"""
print("关闭数据库连接")
self.connection = None
# 如果有异常,回滚事务
if exc_type is not None:
print("发生异常,回滚事务")
else:
print("提交事务")
return False
# 使用
with DatabaseConnection("localhost", "mydb") as conn:
print(f"使用连接: {conn}")
# 执行数据库操作
计时器上下文管理器
import time
class Timer:
"""计时器上下文管理器"""
def __init__(self, name="代码块"):
self.name = name
self.start_time = None
def __enter__(self):
self.start_time = time.time()
return self
def __exit__(self, exc_type, exc_value, traceback):
elapsed = time.time() - self.start_time
print(f"{self.name} 执行时间: {elapsed:.4f}秒")
return False
# 使用
with Timer("数据处理"):
# 模拟耗时操作
time.sleep(1)
result = sum(range(1000000))
# 输出: 数据处理 执行时间: 1.xxxx秒
异常处理上下文管理器
class IgnoreException:
"""忽略特定异常"""
def __init__(self, *exceptions):
self.exceptions = exceptions
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
# 如果异常类型在指定列表中,抑制异常
if exc_type is not None and issubclass(exc_type, self.exceptions):
print(f"忽略异常: {exc_type.__name__}: {exc_value}")
return True # 抑制异常
return False
# 使用
with IgnoreException(ValueError, KeyError):
data = {"a": 1}
value = data["b"] # KeyError,但会被忽略
print("继续执行") # 这行不会执行
print("程序继续") # 这行会执行
# 输出:
# 忽略异常: KeyError: 'b'
# 程序继续
# 5.3、contextlib模块
contextlib模块提供了创建上下文管理器的便捷工具。
@contextmanager装饰器
使用生成器函数创建上下文管理器:
from contextlib import contextmanager
@contextmanager
def file_manager(filename, mode):
"""使用装饰器创建上下文管理器"""
print(f"打开文件: {filename}")
file = open(filename, mode)
try:
yield file # yield前是__enter__,yield后是__exit__
finally:
print(f"关闭文件: {filename}")
file.close()
# 使用
with file_manager("test.txt", "w") as f:
f.write("Hello!")
工作原理
@contextmanager
def my_context():
# __enter__阶段
print("进入")
resource = "资源对象"
try:
yield resource # 返回给as变量
# with块正常结束后继续执行
print("正常退出")
except Exception as e:
# with块抛出异常时执行
print(f"异常退出: {e}")
raise # 重新抛出异常
finally:
# __exit__阶段,无论如何都执行
print("清理资源")
# 使用
with my_context() as res:
print(f"使用: {res}")
实用示例
from contextlib import contextmanager
import time
@contextmanager
def timing(label):
"""计时上下文管理器"""
start = time.time()
try:
yield
finally:
end = time.time()
print(f"{label}: {end - start:.4f}秒")
# 使用
with timing("数据库查询"):
time.sleep(0.5)
# 执行查询
临时修改环境
from contextlib import contextmanager
import os
@contextmanager
def temporary_env(**env_vars):
"""临时设置环境变量"""
old_env = {}
# 保存旧值并设置新值
for key, value in env_vars.items():
old_env[key] = os.environ.get(key)
os.environ[key] = value
try:
yield
finally:
# 恢复旧值
for key, old_value in old_env.items():
if old_value is None:
os.environ.pop(key, None)
else:
os.environ[key] = old_value
# 使用
with temporary_env(DEBUG="true", LOG_LEVEL="debug"):
print(os.environ.get("DEBUG")) # true
# 执行需要特定环境的代码
print(os.environ.get("DEBUG")) # None(已恢复)
suppress(抑制异常)
from contextlib import suppress
# 传统写法
try:
os.remove("somefile.txt")
except FileNotFoundError:
pass
# 使用suppress
with suppress(FileNotFoundError):
os.remove("somefile.txt")
# 抑制多个异常
with suppress(FileNotFoundError, PermissionError):
os.remove("protected_file.txt")
closing(自动关闭)
from contextlib import closing
from urllib.request import urlopen
# 确保对象的close()方法被调用
with closing(urlopen("http://example.com")) as page:
content = page.read()
# 自动调用page.close()
redirect_stdout/redirect_stderr(重定向输出)
from contextlib import redirect_stdout, redirect_stderr
import io
# 重定向标准输出
f = io.StringIO()
with redirect_stdout(f):
print("这些内容会被捕获")
print("不会打印到控制台")
output = f.getvalue()
print(f"捕获的输出: {output}")
# 重定向到文件
with open("output.txt", "w") as f:
with redirect_stdout(f):
print("写入文件")
help(str.upper)
ExitStack(管理多个上下文)
from contextlib import ExitStack
# 动态管理多个上下文
with ExitStack() as stack:
# 动态添加上下文管理器
files = [
stack.enter_context(open(f"file{i}.txt", "w"))
for i in range(5)
]
# 使用所有文件
for i, f in enumerate(files):
f.write(f"内容 {i}\n")
# 所有文件自动关闭
# 条件性添加上下文
def process_files(filenames, use_backup=False):
with ExitStack() as stack:
files = []
for filename in filenames:
file = stack.enter_context(open(filename, "r"))
files.append(file)
if use_backup:
backup = stack.enter_context(
open(f"{filename}.bak", "w")
)
files.append(backup)
# 处理文件
for f in files:
process(f)
# 5.4、嵌套上下文管理器
多个with语句
# 方式1:嵌套with
with open("input.txt", "r") as infile:
with open("output.txt", "w") as outfile:
data = infile.read()
outfile.write(data.upper())
# 方式2:单行with(Python 2.7+)
with open("input.txt", "r") as infile, open("output.txt", "w") as outfile:
data = infile.read()
outfile.write(data.upper())
组合使用
from contextlib import contextmanager
import threading
@contextmanager
def acquire_lock(lock):
"""获取锁"""
print("获取锁")
lock.acquire()
try:
yield
finally:
print("释放锁")
lock.release()
# 使用
lock = threading.Lock()
with acquire_lock(lock):
# 临界区代码
print("执行临界区代码")
# 5.5、资源管理最佳实践
案例1:数据库事务管理
from contextlib import contextmanager
class Database:
def __init__(self):
self.connection = None
@contextmanager
def transaction(self):
"""事务上下文管理器"""
print("开始事务")
try:
yield self
print("提交事务")
# self.connection.commit()
except Exception as e:
print(f"回滚事务: {e}")
# self.connection.rollback()
raise
# 使用
db = Database()
with db.transaction():
# 执行数据库操作
pass
案例2:临时修改配置
from contextlib import contextmanager
class Config:
debug = False
log_level = "INFO"
@contextmanager
def temporary_config(**changes):
"""临时修改配置"""
original = {}
# 保存并修改
for key, value in changes.items():
original[key] = getattr(Config, key)
setattr(Config, key, value)
try:
yield Config
finally:
# 恢复
for key, value in original.items():
setattr(Config, key, value)
# 使用
print(Config.debug) # False
with temporary_config(debug=True, log_level="DEBUG"):
print(Config.debug) # True
print(Config.log_level) # DEBUG
print(Config.debug) # False(已恢复)
案例3:批量操作
from contextlib import contextmanager
@contextmanager
def batch_operation(batch_size=100):
"""批量操作上下文"""
items = []
def add_item(item):
"""添加项目"""
items.append(item)
if len(items) >= batch_size:
process_batch(items)
items.clear()
try:
yield add_item
finally:
# 处理剩余项目
if items:
process_batch(items)
def process_batch(items):
print(f"处理批次: {len(items)} 项")
# 使用
with batch_operation(batch_size=3) as add:
for i in range(10):
add(f"项目{i}")
# 输出:
# 处理批次: 3 项
# 处理批次: 3 项
# 处理批次: 3 项
# 处理批次: 1 项
案例4:性能分析
from contextlib import contextmanager
import time
import functools
@contextmanager
def profile_section(name):
"""性能分析上下文"""
start_time = time.time()
start_memory = 0 # 简化示例
try:
yield
finally:
elapsed = time.time() - start_time
print(f"{name}:")
print(f" 时间: {elapsed:.4f}秒")
# 使用
with profile_section("数据处理"):
data = [i ** 2 for i in range(1000000)]
with profile_section("数据保存"):
time.sleep(0.1)
案例5:资源池管理
from contextlib import contextmanager
from queue import Queue
class ConnectionPool:
"""连接池"""
def __init__(self, size=5):
self.pool = Queue(maxsize=size)
for i in range(size):
self.pool.put(f"Connection_{i}")
@contextmanager
def get_connection(self):
"""获取连接"""
conn = self.pool.get()
print(f"获取连接: {conn}")
try:
yield conn
finally:
print(f"归还连接: {conn}")
self.pool.put(conn)
# 使用
pool = ConnectionPool(size=2)
with pool.get_connection() as conn1:
print(f"使用 {conn1}")
with pool.get_connection() as conn2:
print(f"使用 {conn2}")
小结对比表
| 特性 | Python上下文管理器 | Java try-with-resources |
|---|---|---|
| 语法 | with obj as var: | try (Type var = ...) {} |
| 协议 | __enter__/__exit__ | AutoCloseable.close() |
| 自定义 | 类或@contextmanager | 实现AutoCloseable |
| 嵌套 | 支持 | 支持 |
| 异常抑制 | 支持(__exit__返回True) | 不支持 |
| 多资源 | with a, b: | try (A a; B b) |
# 6、函数式编程
说明:列表推导式和生成器表达式已在第5章"数据结构与推导式"中详细讲解,这里不再重复。
Python虽然不是纯函数式语言,但提供了丰富的函数式编程工具。对于熟悉Java 8+ Stream API的开发者来说,Python的函数式特性会感觉很亲切。
# 6.1、map()、filter()、reduce()
这三个函数是函数式编程的基石。
// Java Stream API方式
List<Integer> squares = IntStream.range(0, 10)
.map(x -> x * x)
.boxed()
.collect(Collectors.toList());
List<Integer> evenSquares = IntStream.range(0, 10)
.filter(x -> x % 2 == 0)
.map(x -> x * x)
.boxed()
.collect(Collectors.toList());
// 多重循环 - Java需要嵌套Stream或flatMap
List<String> pairs = IntStream.rangeClosed(1, 3)
.boxed()
.flatMap(x -> Stream.of("a", "b", "c")
.map(y -> "(" + x + ", " + y + ")"))
.collect(Collectors.toList());
字符串处理示例:
# 提取所有单词的首字母
sentence = "Hello World Python Programming"
initials = [word[0] for word in sentence.split()]
print(initials) # ['H', 'W', 'P', 'P']
# 过滤并转换
words = ["apple", "banana", "cherry", "date"]
upper_long_words = [w.upper() for w in words if len(w) > 5]
print(upper_long_words) # ['BANANA', 'CHERRY']
# 嵌套列表展平
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 6.2、字典推导式
字典推导式让创建字典变得极其简单。
# 基本字典推导式
# 格式:{key表达式: value表达式 for 变量 in 可迭代对象}
squares_dict = {x: x**2 for x in range(6)}
print(squares_dict) # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# 从两个列表创建字典
keys = ['name', 'age', 'city']
values = ['Alice', 25, 'Beijing']
person = {k: v for k, v in zip(keys, values)}
print(person) # {'name': 'Alice', 'age': 25, 'city': 'Beijing'}
# 带条件的字典推导式
numbers = [1, 2, 3, 4, 5, 6]
even_squares = {x: x**2 for x in numbers if x % 2 == 0}
print(even_squares) # {2: 4, 4: 16, 6: 36}
# 字典键值互换
original = {'a': 1, 'b': 2, 'c': 3}
swapped = {v: k for k, v in original.items()}
print(swapped) # {1: 'a', 2: 'b', 3: 'c'}
实际应用场景:
# 统计字符频率
text = "hello world"
char_count = {char: text.count(char) for char in set(text) if char != ' '}
print(char_count) # {'h': 1, 'e': 1, 'l': 3, 'o': 2, 'w': 1, 'r': 1, 'd': 1}
# 过滤字典
scores = {'Alice': 85, 'Bob': 92, 'Charlie': 78, 'David': 95}
high_scores = {name: score for name, score in scores.items() if score >= 90}
print(high_scores) # {'Bob': 92, 'David': 95}
# 转换字典值
prices_usd = {'apple': 1.5, 'banana': 0.8, 'orange': 2.0}
exchange_rate = 6.5
prices_cny = {item: price * exchange_rate for item, price in prices_usd.items()}
print(prices_cny) # {'apple': 9.75, 'banana': 5.2, 'orange': 13.0}
Java对比:
// Java创建Map - 相对繁琐
Map<Integer, Integer> squares = IntStream.range(0, 6)
.boxed()
.collect(Collectors.toMap(x -> x, x -> x * x));
// 过滤Map
Map<String, Integer> highScores = scores.entrySet().stream()
.filter(e -> e.getValue() >= 90)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
# 6.3、集合推导式
集合推导式用于创建去重的��合。
# 基本集合推导式
# 格式:{表达式 for 变量 in 可迭代对象}
unique_chars = {char.lower() for char in "Hello World"}
print(unique_chars) # {'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'}
# 带条件
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
even_unique = {x for x in numbers if x % 2 == 0}
print(even_unique) # {2, 4}
# 数学运算
squares_set = {x**2 for x in range(-5, 6)}
print(squares_set) # {0, 1, 4, 9, 16, 25} - 自动去重
# 从字符串列表提取所有唯一字符
words = ['apple', 'banana', 'cherry']
all_chars = {char for word in words for char in word}
print(all_chars) # {'a', 'b', 'c', 'e', 'h', 'l', 'n', 'p', 'r', 'y'}
实际应用:
# 提取邮箱域名
emails = ['user1@gmail.com', 'user2@qq.com', 'user3@gmail.com', 'user4@163.com']
domains = {email.split('@')[1] for email in emails}
print(domains) # {'gmail.com', 'qq.com', '163.com'}
# 查找重复元素
numbers = [1, 2, 3, 2, 4, 3, 5]
seen = set()
duplicates = {x for x in numbers if x in seen or seen.add(x)}
print(duplicates) # {2, 3}
# 6.4、生成器表达式
生成器表达式语法类似列表推导式,但使用圆括号。它不会立即计算所有值,而是按需生成,非常节省内存。
# 列表推导式 vs 生成器表达式
import sys
# 列表推导式 - 立即创建所有元素
list_comp = [x**2 for x in range(1000000)]
print(f"列表大小: {sys.getsizeof(list_comp)} 字节") # 约8MB
# 生成器表达式 - 只在需要时生成元素
gen_exp = (x**2 for x in range(1000000))
print(f"生成器大小: {sys.getsizeof(gen_exp)} 字节") # 约200字节
# 使用生成器
for i, value in enumerate(gen_exp):
if i >= 5:
break
print(value, end=' ') # 0 1 4 9 16
生成器表达式的优势:
# 1. 内存效率 - 处理大文件
# 错误方式 - 可能内存溢出
def read_file_bad(filename):
return [line.strip() for line in open(filename)]
# 正确方式 - 使用生成器
def read_file_good(filename):
return (line.strip() for line in open(filename))
# 2. 无限序列
def fibonacci():
"""斐波那契数列生成器"""
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 使用生成器表达式过滤
fib_gen = fibonacci()
even_fibs = (x for x in fib_gen if x % 2 == 0)
# 获取前5个偶数斐波那契数
result = []
for _ in range(5):
result.append(next(even_fibs))
print(result) # [0, 2, 8, 34, 144]
# 3. 链式处理
numbers = range(100)
# 每一步都是惰性求值
pipeline = (x for x in numbers if x % 2 == 0) # 过滤偶数
pipeline = (x**2 for x in pipeline) # 平方
pipeline = (x for x in pipeline if x > 100) # 过滤大于100的
print(list(pipeline)[:5]) # [144, 196, 256, 324, 400]
与Java Stream的对比:
# Python生成器表达式
numbers = range(1000000)
result = sum(x**2 for x in numbers if x % 2 == 0)
print(result)
// Java Stream - 概念类似
long result = IntStream.range(0, 1000000)
.filter(x -> x % 2 == 0)
.mapToLong(x -> (long) x * x)
.sum();
# 6.5、实战案例
案例1:数据清洗
# 清洗用户输入数据
raw_data = [
" Alice ",
"bob",
" CHARLIE",
"",
" ",
"david "
]
# 清洗:去空格、转小写、过滤空值
cleaned = [name.strip().lower() for name in raw_data if name.strip()]
print(cleaned) # ['alice', 'bob', 'charlie', 'david']
# 生成用户字典
users = {
i: {'id': i, 'name': name, 'email': f"{name}@example.com"}
for i, name in enumerate(cleaned, 1)
}
print(users)
# {1: {'id': 1, 'name': 'alice', 'email': 'alice@example.com'}, ...}
案例2:矩阵转置
# 使用列表推导式转置矩阵
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# 转置
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print(transposed)
# [[1, 4, 7],
# [2, 5, 8],
# [3, 6, 9]]
# 或者使用zip(更Pythonic)
transposed2 = [list(col) for col in zip(*matrix)]
print(transposed2) # 同上
案例3:文件批处理
import os
# 批量重命名文件(模拟)
files = ['report_2024.txt', 'data_2024.csv', 'summary_2024.pdf']
# 创建重命名映射
rename_map = {
old: old.replace('2024', '2025')
for old in files
if '2024' in old
}
print(rename_map)
# {'report_2024.txt': 'report_2025.txt',
# 'data_2024.csv': 'data_2025.csv',
# 'summary_2024.pdf': 'summary_2025.pdf'}
# 实际重命名(示例)
# for old, new in rename_map.items():
# os.rename(old, new)
案例4:配置解析
# 解析配置行
config_lines = [
"name=Alice",
"age=25",
"# 这是注释",
"city=Beijing",
"",
"email=alice@example.com"
]
# 解析为字典,跳过注释和空行
config = {
line.split('=')[0]: line.split('=')[1]
for line in config_lines
if line and not line.startswith('#') and '=' in line
}
print(config)
# {'name': 'Alice', 'age': '25', 'city': 'Beijing', 'email': 'alice@example.com'}
案例5:SQL结果处理
# 模拟数据库查询结果
db_results = [
('Alice', 85, 'Math'),
('Bob', 92, 'Math'),
('Charlie', 78, 'English'),
('David', 95, 'Math'),
('Eve', 88, 'English')
]
# 转换为字典列表
students = [
{'name': name, 'score': score, 'subject': subject}
for name, score, subject in db_results
]
# 按科目分组
from collections import defaultdict
by_subject = defaultdict(list)
for student in students:
by_subject[student['subject']].append(student)
# 或使用字典推导式计算每科平均分
avg_scores = {
subject: sum(s['score'] for s in students if s['subject'] == subject) /
len([s for s in students if s['subject'] == subject])
for subject in {s['subject'] for s in students}
}
print(avg_scores) # {'Math': 90.666..., 'English': 83.0}
案例6:数据聚合
# 电商订单数据
orders = [
{'id': 1, 'user': 'Alice', 'amount': 100, 'status': 'paid'},
{'id': 2, 'user': 'Bob', 'amount': 200, 'status': 'paid'},
{'id': 3, 'user': 'Alice', 'amount': 150, 'status': 'pending'},
{'id': 4, 'user': 'Charlie', 'amount': 300, 'status': 'paid'},
{'id': 5, 'user': 'Bob', 'amount': 250, 'status': 'cancelled'}
]
# 统计每个用户的已支付订单总额
user_totals = {
user: sum(order['amount'] for order in orders
if order['user'] == user and order['status'] == 'paid')
for user in {order['user'] for order in orders}
}
print(user_totals) # {'Alice': 100, 'Bob': 200, 'Charlie': 300}
# 获取所有已支付订单ID
paid_ids = [order['id'] for order in orders if order['status'] == 'paid']
print(paid_ids) # [1, 2, 4]
# 6.6、性能对比
import timeit
# 测试1:列表推导式 vs for循环
def using_for_loop():
result = []
for x in range(1000):
if x % 2 == 0:
result.append(x**2)
return result
def using_list_comp():
return [x**2 for x in range(1000) if x % 2 == 0]
time_loop = timeit.timeit(using_for_loop, number=10000)
time_comp = timeit.timeit(using_list_comp, number=10000)
print(f"for循环耗时: {time_loop:.4f}秒")
print(f"列表推导式耗时: {time_comp:.4f}秒")
print(f"性能提升: {(time_loop/time_comp - 1) * 100:.1f}%")
# 列表推导式通常快20-30%
# 测试2:生成器 vs 列表(内存使用)
import sys
list_result = [x**2 for x in range(100000)]
gen_result = (x**2 for x in range(100000))
print(f"列表内存: {sys.getsizeof(list_result):,} 字节") # 约800KB
print(f"生成器内存: {sys.getsizeof(gen_result):,} 字节") # 约200字节
print(f"内存节省: {(1 - sys.getsizeof(gen_result)/sys.getsizeof(list_result)) * 100:.1f}%")
# 6.7、最佳实践
1. 可读性优先
# 不推荐 - 过度复杂
result = [x**2 for x in range(100) if x % 2 == 0 if x % 3 == 0 if x > 10]
# 推荐 - 拆分逻辑
result = [
x**2
for x in range(100)
if x % 2 == 0 and x % 3 == 0 and x > 10
]
# 或者使用普通循环
result = []
for x in range(100):
if x % 2 == 0 and x % 3 == 0 and x > 10:
result.append(x**2)
2. 何时使用生成器
# 使用列表 - 需要多次访问
numbers = [x**2 for x in range(100)]
print(sum(numbers))
print(max(numbers)) # 可以重复使用
# 使用生成器 - 只遍历一次,或数据量大
total = sum(x**2 for x in range(1000000)) # 一次性消费,节省内存
# 处理大文件 - 必须用生成器
lines = (line.strip() for line in open('large_file.txt'))
errors = (line for line in lines if 'ERROR' in line)
3. 避免副作用
# 错误 - 推导式中有副作用
counter = 0
result = [counter := counter + 1 for x in range(10)] # 不推荐
# 正确 - 使用enumerate
result = [i for i, x in enumerate(range(10))]
对比总结:
| 特性 | Python推导式 | Java Stream API |
|---|---|---|
| 列表创建 | [x*2 for x in list] | list.stream().map(x -> x*2).collect() |
| 过滤 | [x for x in list if x > 0] | list.stream().filter(x -> x > 0).collect() |
| 字典创建 | {k: v for k, v in pairs} | pairs.stream().collect(toMap(...)) |
| 去重 | {x for x in list} | list.stream().distinct().collect() |
| 惰性求值 | 生成器表达式 (x for x in list) | Stream默认惰性 |
| 语法简洁度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 可读性 | 高(简单场景) | 中等 |
| 性能 | 快(C优化) | 快(JIT优化) |
# 7、函数式编程
Python虽然不是纯函数式语言,但提供了丰富的函数式编程工具。对于熟悉Java 8+ Stream API的开发者来说,Python的函数式特性会感觉很亲切。
# 7.1、高阶函数
说明:map()、filter()、reduce()已在第6章"函数式编程"中详细讲解,这里介绍其他高阶函数。
函数作为参数 numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 五、过滤偶数
evens = list(filter(lambda x: x % 2 == 0, numbers)) print(evens) # [2, 4, 6, 8, 10]
# 六、过滤非空字符串
words = ['hello', '', 'world', ' ', 'python'] non_empty = list(filter(lambda s: s.strip(), words)) print(non_empty) # ['hello', 'world', 'python']
# 七、过滤None值
data = [1, None, 2, None, 3, 4, None] filtered = list(filter(None, data)) # None作为函数,过滤假值 print(filtered) # [1, 2, 3, 4]
# 八、实际应用:过滤有效邮箱
emails = ['user@example.com', 'invalid', 'admin@test.com', 'bad@'] valid = list(filter(lambda e: '@' in e and '.' in e.split('@')[1], emails)) print(valid) # ['user@example.com', 'admin@test.com']
**reduce() - 累积计算**
```python
from functools import reduce
# reduce(function, iterable[, initializer])
numbers = [1, 2, 3, 4, 5]
# 求和
total = reduce(lambda x, y: x + y, numbers)
print(total) # 15
# 求积
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
# 查找最大值
maximum = reduce(lambda x, y: x if x > y else y, numbers)
print(maximum) # 5
# 带初始值
total_with_init = reduce(lambda x, y: x + y, numbers, 100)
print(total_with_init) # 115
# 实际应用:合并字典
dicts = [
{'a': 1, 'b': 2},
{'c': 3, 'd': 4},
{'e': 5}
]
merged = reduce(lambda d1, d2: {**d1, **d2}, dicts)
print(merged) # {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
# 扁平化嵌套列表
nested = [[1, 2], [3, 4], [5, 6]]
flat = reduce(lambda acc, lst: acc + lst, nested, [])
print(flat) # [1, 2, 3, 4, 5, 6]
Java对比:
// Java reduce
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
// 或使用方法引用
int sum = numbers.stream()
.reduce(0, Integer::sum);
三剑客组合使用:
# 计算1-100中所有偶数的平方和
numbers = range(1, 101)
result = reduce(
lambda x, y: x + y,
map(lambda x: x**2,
filter(lambda x: x % 2 == 0, numbers))
)
print(result) # 171700
# 更Pythonic的方式(推导式)
result = sum(x**2 for x in range(1, 101) if x % 2 == 0)
print(result) # 171700
# 1、高阶函数
高阶函数是指接受函数作为参数或返回函数的函数。
函数作为参数:
def apply_operation(numbers, operation):
"""对列表中每个数字应用操作"""
return [operation(x) for x in numbers]
# 定义不同的操作
def square(x):
return x ** 2
def double(x):
return x * 2
def negate(x):
return -x
numbers = [1, 2, 3, 4, 5]
print(apply_operation(numbers, square)) # [1, 4, 9, 16, 25]
print(apply_operation(numbers, double)) # [2, 4, 6, 8, 10]
print(apply_operation(numbers, negate)) # [-1, -2, -3, -4, -5]
# 使用lambda
print(apply_operation(numbers, lambda x: x**3)) # [1, 8, 27, 64, 125]
函数作为返回值:
def make_multiplier(n):
"""返回一个乘以n的函数"""
def multiplier(x):
return x * n
return multiplier
times2 = make_multiplier(2)
times10 = make_multiplier(10)
print(times2(5)) # 10
print(times10(5)) # 50
# 实际应用:创建验证器
def make_range_validator(min_val, max_val):
"""创建范围验证函数"""
def validator(value):
return min_val <= value <= max_val
return validator
age_validator = make_range_validator(0, 120)
percentage_validator = make_range_validator(0, 100)
print(age_validator(25)) # True
print(age_validator(150)) # False
print(percentage_validator(85)) # True
内置高阶函数:
# sorted() - 自定义排序
students = [
{'name': 'Alice', 'age': 25, 'score': 85},
{'name': 'Bob', 'age': 22, 'score': 92},
{'name': 'Charlie', 'age': 23, 'score': 78}
]
# 按年龄排序
by_age = sorted(students, key=lambda s: s['age'])
print([s['name'] for s in by_age]) # ['Bob', 'Charlie', 'Alice']
# 按分数降序
by_score = sorted(students, key=lambda s: s['score'], reverse=True)
print([s['name'] for s in by_score]) # ['Bob', 'Alice', 'Charlie']
# 多级排序:先按分数降序,再按年龄升序
multi_sort = sorted(students, key=lambda s: (-s['score'], s['age']))
# max()/min() - 自定义比较
oldest = max(students, key=lambda s: s['age'])
print(oldest['name']) # Alice
highest_score = max(students, key=lambda s: s['score'])
print(highest_score['name']) # Bob
# 2、偏函数(functools.partial)
偏函数用于固定函数的某些参数,创建新函数。
from functools import partial
# 基本示例
def power(base, exponent):
return base ** exponent
# 创建偏函数 - 固定exponent=2
square = partial(power, exponent=2)
print(square(5)) # 25
print(square(10)) # 100
# 固定exponent=3
cube = partial(power, exponent=3)
print(cube(5)) # 125
# 实际应用1:日志记录
def log_message(message, level='INFO', timestamp=True):
import datetime
prefix = f"[{datetime.datetime.now()}] " if timestamp else ""
print(f"{prefix}{level}: {message}")
# 创建专用日志函数
log_error = partial(log_message, level='ERROR')
log_warning = partial(log_message, level='WARNING')
log_debug = partial(log_message, level='DEBUG', timestamp=False)
log_error("Database connection failed")
log_warning("Low memory")
log_debug("Variable value: 42")
# 实际应用2:数据转换
def convert_value(value, multiplier=1, offset=0, round_digits=2):
"""通用数值转换函数"""
result = value * multiplier + offset
return round(result, round_digits)
# 摄氏度转华氏度: F = C * 9/5 + 32
celsius_to_fahrenheit = partial(convert_value, multiplier=9/5, offset=32)
print(celsius_to_fahrenheit(0)) # 32.0
print(celsius_to_fahrenheit(100)) # 212.0
# 米转英尺: feet = meter * 3.28084
meter_to_feet = partial(convert_value, multiplier=3.28084)
print(meter_to_feet(10)) # 32.81
# 实际应用3:配置HTTP请求
import functools
def make_request(url, method='GET', headers=None, timeout=30):
"""模拟HTTP请求"""
headers = headers or {}
print(f"{method} {url}")
print(f"Headers: {headers}")
print(f"Timeout: {timeout}s")
# API特定配置
api_headers = {'Authorization': 'Bearer token123', 'Content-Type': 'application/json'}
api_request = partial(make_request, headers=api_headers, timeout=60)
# 使用
api_request('https://api.example.com/users')
api_request('https://api.example.com/posts', method='POST')
与Java对比:
// Java没有直接的偏函数,需要手动包装
BiFunction<Integer, Integer, Integer> power = (base, exp) -> (int) Math.pow(base, exp);
// 创建"偏函数"
Function<Integer, Integer> square = base -> power.apply(base, 2);
Function<Integer, Integer> cube = base -> power.apply(base, 3);
# 3、函数组合
函数组合是将多个函数组合成一个新函数。
# 手动实现函数组合
def compose(*functions):
"""从右到左组合函数"""
def inner(arg):
result = arg
for func in reversed(functions):
result = func(result)
return result
return inner
# 定义基础函数
def add_one(x):
return x + 1
def double(x):
return x * 2
def square(x):
return x ** 2
# 组合函数:(x+1) * 2 然后平方
combined = compose(square, double, add_one)
print(combined(3)) # ((3+1)*2)^2 = 64
# 更优雅的实现
from functools import reduce
def compose2(*functions):
"""使用reduce实现函数组合"""
return reduce(lambda f, g: lambda x: f(g(x)), functions)
combined2 = compose2(square, double, add_one)
print(combined2(3)) # 64
# 实际应用:数据处理管道
def remove_spaces(text):
return text.replace(' ', '')
def to_lowercase(text):
return text.lower()
def remove_punctuation(text):
import string
return text.translate(str.maketrans('', '', string.punctuation))
# 组合文本清洗函数
clean_text = compose(remove_punctuation, to_lowercase, remove_spaces)
text = "Hello, World! Python is AWESOME."
print(clean_text(text)) # heloworldpythonisawesome
# 更Pythonic的方式:使用管道
class Pipeline:
"""函数管道"""
def __init__(self, value):
self.value = value
def pipe(self, func):
"""应用函数并返回新的Pipeline"""
return Pipeline(func(self.value))
def get(self):
"""获取最终值"""
return self.value
# 使用管道
result = (Pipeline("Hello, World!")
.pipe(str.lower)
.pipe(lambda s: s.replace(' ', ''))
.pipe(lambda s: s.replace('!', ''))
.get())
print(result) # helloworld
实战:数据转换管道
# 处理用户数据
users = [
{'name': ' ALICE ', 'age': '25', 'email': 'ALICE@EXAMPLE.COM'},
{'name': 'bob', 'age': '30', 'email': 'bob@example.com '},
{'name': ' Charlie ', 'age': '22', 'email': ' charlie@EXAMPLE.com'}
]
# 定义转换函数
def clean_name(user):
user['name'] = user['name'].strip().capitalize()
return user
def convert_age(user):
user['age'] = int(user['age'])
return user
def normalize_email(user):
user['email'] = user['email'].strip().lower()
return user
# 组合所有转换
from functools import reduce
def process_user(user):
transformations = [clean_name, convert_age, normalize_email]
return reduce(lambda u, transform: transform(u), transformations, user)
# 应用到所有用户
processed = list(map(process_user, users))
for user in processed:
print(user)
# {'name': 'Alice', 'age': 25, 'email': 'alice@example.com'}
# {'name': 'Bob', 'age': 30, 'email': 'bob@example.com'}
# {'name': 'Charlie', 'age': 22, 'email': 'charlie@example.com'}
# 4、纯函数与副作用
纯函数是函数式编程的核心概念。
纯函数的特征:
# 纯函数 - 相同输入总是产生相同输出,无副作用
def add(a, b):
return a + b
def multiply(a, b):
return a * b
# 纯函数 - 不修改输入
def append_item_pure(lst, item):
"""返回新列表,不修改原列表"""
return lst + [item]
original = [1, 2, 3]
new_list = append_item_pure(original, 4)
print(original) # [1, 2, 3] - 未被修改
print(new_list) # [1, 2, 3, 4]
# 非纯函数 - 有副作用
def append_item_impure(lst, item):
"""直接修改列表"""
lst.append(item)
return lst
original = [1, 2, 3]
new_list = append_item_impure(original, 4)
print(original) # [1, 2, 3, 4] - 被修改了!
print(new_list) # [1, 2, 3, 4]
# 非纯函数 - 依赖外部状态
counter = 0
def increment_impure():
global counter
counter += 1
return counter
print(increment_impure()) # 1
print(increment_impure()) # 2 - 相同调用,不同结果!
# 纯函数替代方案
def increment_pure(value):
return value + 1
counter = 0
counter = increment_pure(counter) # 1
counter = increment_pure(counter) # 2
纯函数的优势:
# 1. 可测试性
def calculate_total(items, tax_rate):
"""纯函数 - 易于测试"""
subtotal = sum(item['price'] * item['quantity'] for item in items)
return subtotal * (1 + tax_rate)
# 测试简单
items = [
{'price': 10, 'quantity': 2},
{'price': 5, 'quantity': 3}
]
assert calculate_total(items, 0.1) == 38.5
# 2. 可组合性
def filter_active(users):
return [u for u in users if u.get('active', False)]
def sort_by_name(users):
return sorted(users, key=lambda u: u['name'])
def get_emails(users):
return [u['email'] for u in users]
# 轻松组合
users = [
{'name': 'Alice', 'active': True, 'email': 'alice@example.com'},
{'name': 'Bob', 'active': False, 'email': 'bob@example.com'},
{'name': 'Charlie', 'active': True, 'email': 'charlie@example.com'}
]
active_emails = get_emails(sort_by_name(filter_active(users)))
print(active_emails) # ['alice@example.com', 'charlie@example.com']
# 3. 并发安全
from concurrent.futures import ThreadPoolExecutor
def square_pure(x):
"""纯函数 - 线程安全"""
return x ** 2
numbers = range(100)
with ThreadPoolExecutor(max_workers=4) as executor:
results = list(executor.map(square_pure, numbers))
避免副作用的技巧:
# 1. 使用copy避免修改原始数据
import copy
def update_user_pure(user, **updates):
"""返回更新后的新用户对象"""
new_user = copy.deepcopy(user)
new_user.update(updates)
return new_user
user = {'name': 'Alice', 'age': 25}
updated = update_user_pure(user, age=26)
print(user) # {'name': 'Alice', 'age': 25}
print(updated) # {'name': 'Alice', 'age': 26}
# 2. 使用不可变数据结构
from collections import namedtuple
User = namedtuple('User', ['name', 'age', 'email'])
user = User('Alice', 25, 'alice@example.com')
# 创建新对象而不是修改
updated = user._replace(age=26)
print(user) # User(name='Alice', age=25, ...)
print(updated) # User(name='Alice', age=26, ...)
# 3. 使用dataclasses(Python 3.7+)
from dataclasses import dataclass, replace
@dataclass(frozen=True) # frozen=True使其不可变
class Product:
name: str
price: float
quantity: int
product = Product('Book', 29.99, 10)
# product.price = 19.99 # 错误!不可修改
# 使用replace创建新对象
discounted = replace(product, price=19.99)
print(product) # Product(name='Book', price=29.99, quantity=10)
print(discounted) # Product(name='Book', price=19.99, quantity=10)
# 5、实战案例
案例1:函数式风格的数据处理
from functools import reduce
from operator import itemgetter
# 订单数据
orders = [
{'id': 1, 'user_id': 101, 'amount': 150.0, 'status': 'completed'},
{'id': 2, 'user_id': 102, 'amount': 200.0, 'status': 'pending'},
{'id': 3, 'user_id': 101, 'amount': 80.0, 'status': 'completed'},
{'id': 4, 'user_id': 103, 'amount': 300.0, 'status': 'completed'},
{'id': 5, 'user_id': 102, 'amount': 120.0, 'status': 'cancelled'}
]
# 需求:计算所有已完成订单的总金额,并按用户分组
# 1. 过滤已完成订单
completed_orders = list(filter(lambda o: o['status'] == 'completed', orders))
# 2. 提取金额
amounts = list(map(itemgetter('amount'), completed_orders))
# 3. 计算总额
total = reduce(lambda x, y: x + y, amounts, 0)
print(f"已完成订单总额: ${total}") # $530.0
# 按用户分组统计
from collections import defaultdict
def group_by_user(orders):
grouped = defaultdict(list)
for order in orders:
grouped[order['user_id']].append(order)
return dict(grouped)
user_orders = group_by_user(completed_orders)
user_totals = {
user_id: reduce(lambda acc, o: acc + o['amount'], orders, 0)
for user_id, orders in user_orders.items()
}
print(user_totals) # {101: 230.0, 103: 300.0}
案例2:函数管道处理文本
# 文本分析管道
def read_words(text):
"""分割为单词"""
return text.lower().split()
def remove_short_words(words, min_length=3):
"""移除短单词"""
return [w for w in words if len(w) >= min_length]
def remove_punctuation(words):
"""移除标点符号"""
import string
translator = str.maketrans('', '', string.punctuation)
return [w.translate(translator) for w in words]
def count_words(words):
"""统计词频"""
from collections import Counter
return Counter(words)
def top_words(counter, n=5):
"""获取最常见的n个单词"""
return counter.most_common(n)
# 组合所有步骤
def analyze_text(text, min_length=3, top_n=5):
return (top_words(
count_words(
remove_punctuation(
remove_short_words(
read_words(text),
min_length
)
)
),
top_n
))
text = """
Python is an amazing programming language. Python is used for web development,
data science, and machine learning. Many developers love Python because Python
is easy to learn and very powerful.
"""
result = analyze_text(text)
for word, count in result:
print(f"{word}: {count}")
# python: 4
# is: 3
# and: 2
# ...
对比总结:
| 特性 | Python | Java |
|---|---|---|
| map/filter/reduce | 内置函数 | Stream API |
| Lambda表达式 | lambda x: x*2 | x -> x*2 |
| 高阶函数 | 原生支持 | 支持(函数式接口) |
| 偏函数 | functools.partial | 手动实现 |
| 函数组合 | 需手动实现 | 需手动实现 |
| 不可变性 | 可选(tuple, frozenset) | 可选(final, Collections.unmodifiable) |
| 纯函数 | 约定优于强制 | 约定优于强制 |
| 函数式库 | functools, itertools, operator | java.util.function, Stream API |
# 6、类型提示(Type Hints)
作为Java开发者,你习惯了强类型系统。Python从3.5版本开始引入了类型提示,虽然不强制检查,但能让代码更清晰、IDE提示更好。
# 6.1、基本类型注解
# 变量类型注解
name: str = "Alice"
age: int = 25
salary: float = 5000.50
is_active: bool = True
# 函数参数和返回值注解
def greet(name: str, age: int) -> str:
return f"Hello {name}, you are {age} years old"
def add(a: int, b: int) -> int:
return a + b
def get_user_info() -> dict:
return {"name": "Alice", "age": 25}
# 无返回值
def log_message(message: str) -> None:
print(message)
# Java对比
"""
// Java强制类型
String name = "Alice";
int age = 25;
public String greet(String name, int age) {
return "Hello " + name;
}
"""
# 6.2、复合类型注解
from typing import List, Dict, Tuple, Set
# 列表类型
numbers: List[int] = [1, 2, 3, 4]
names: List[str] = ["Alice", "Bob"]
# 字典类型
user: Dict[str, int] = {"age": 25, "score": 90}
config: Dict[str, any] = {"host": "localhost", "port": 8080}
# 元组类型
point: Tuple[int, int] = (10, 20)
person: Tuple[str, int, bool] = ("Alice", 25, True)
# 集合类型
unique_ids: Set[int] = {1, 2, 3}
# 嵌套类型
users: List[Dict[str, any]] = [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30}
]
# 实际应用
def get_user_scores() -> Dict[str, List[int]]:
"""返回用户和他们的分数列表"""
return {
"Alice": [85, 90, 88],
"Bob": [92, 87, 95]
}
def process_data(items: List[Tuple[str, int]]) -> Dict[str, int]:
"""处理数据列表"""
return {name: score for name, score in items}
# 6.3、Optional与Union
from typing import Optional, Union
# Optional - 可能为None
def find_user(user_id: int) -> Optional[dict]:
"""返回用户或None"""
if user_id > 0:
return {"id": user_id, "name": "Alice"}
return None
# Optional[X] 等价于 Union[X, None]
def get_config(key: str) -> Optional[str]:
return None
# Union - 多种可能类型
def process_value(value: Union[int, str, float]) -> str:
"""处理int、str或float类型"""
return str(value)
# 实际应用
def parse_input(data: Union[str, bytes]) -> str:
"""处理字符串或字节"""
if isinstance(data, bytes):
return data.decode('utf-8')
return data
# Java对比
"""
// Java使用泛型和null
Optional<User> findUser(int id) {
if (id > 0) {
return Optional.of(new User(id));
}
return Optional.empty();
}
"""
# 6.4、泛型类型
from typing import TypeVar, Generic, List
# 定义类型变量
T = TypeVar('T')
# 泛型函数
def first_element(items: List[T]) -> Optional[T]:
"""返回列表第一个元素"""
return items[0] if items else None
# 泛型类
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) -> Optional[T]:
return self._items.pop() if self._items else None
# 使用
int_stack: Stack[int] = Stack()
int_stack.push(1)
int_stack.push(2)
str_stack: Stack[str] = Stack()
str_stack.push("hello")
# 6.5、typing模块常用类型
from typing import Callable, Any, Sequence, Mapping, Iterable
# Callable - 可调用对象
def apply_func(func: Callable[[int, int], int], a: int, b: int) -> int:
return func(a, b)
result = apply_func(lambda x, y: x + y, 3, 5) # 8
# Any - 任意类型
def process_data(data: Any) -> str:
return str(data)
# Sequence - 序列类型
def sum_values(numbers: Sequence[int]) -> int:
return sum(numbers)
sum_values([1, 2, 3]) # List
sum_values((1, 2, 3)) # Tuple
# Mapping - 映射类型
def print_config(config: Mapping[str, any]) -> None:
for key, value in config.items():
print(f"{key}: {value}")
# Iterable - 可迭代对象
def process_items(items: Iterable[str]) -> List[str]:
return [item.upper() for item in items]
# 6.6、类型别名
from typing import List, Dict, Tuple
# 定义类型别名
UserId = int
UserName = str
Score = float
# 复杂类型别名
User = Dict[str, any]
UserList = List[User]
Coordinate = Tuple[float, float]
ScoreBoard = Dict[UserName, List[Score]]
# 使用类型别名
def get_user(user_id: UserId) -> User:
return {"id": user_id, "name": "Alice"}
def calculate_average(scores: ScoreBoard) -> Dict[UserName, Score]:
return {
name: sum(score_list) / len(score_list)
for name, score_list in scores.items()
}
# 6.7、mypy静态类型检查
# 安装mypy: pip install mypy
# 示例代码 example.py
def add_numbers(a: int, b: int) -> int:
return a + b
result: str = add_numbers(1, 2) # 类型错误!
# 运行检查
# mypy example.py
# error: Incompatible types in assignment (expression has type "int", variable has type "str")
# 配置mypy: mypy.ini
"""
[mypy]
python_version = 3.9
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
"""
# 实际项目示例
from typing import List, Optional
class UserService:
"""用户服务类"""
def __init__(self, db_connection: any) -> None:
self.db = db_connection
def find_user(self, user_id: int) -> Optional[Dict[str, any]]:
"""查找用户"""
# 实现...
return {"id": user_id, "name": "Alice"}
def get_all_users(self) -> List[Dict[str, any]]:
"""获取所有用户"""
return [{"id": 1, "name": "Alice"}]
对比总结:
| 特性 | Python类型提示 | Java类型系统 |
|---|---|---|
| 强制性 | 可选,运行时不检查 | 强制,编译时检查 |
| 基本语法 | name: str | String name |
| 泛型 | List[int] | List<Integer> |
| 可选类型 | Optional[int] | Optional<Integer> |
| 联合类型 | Union[int, str] | 不直接支持 |
| 类型别名 | UserId = int | typedef(C++)或接口 |
| 检查工具 | mypy, pyright | javac内置 |
| IDE支持 | VSCode, PyCharm | IntelliJ IDEA |
# 7、反射与内省
Python的反射能力比Java更强大。作为Java开发者,你会发现Python的反射API更简单直接。
# 7.1、inspect模块
import inspect
# 定义示例类
class User:
"""用户类"""
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def greet(self):
return f"Hello, I'm {self.name}"
@staticmethod
def create_guest():
return User("Guest", 0)
# 检查对象类型
print(inspect.isclass(User)) # True
print(inspect.isfunction(User.greet)) # True
print(inspect.ismethod(User().greet)) # True
# 获取源代码
print(inspect.getsource(User)) # 打印User类的源代码
print(inspect.getfile(User)) # 获取文件路径
# 获取函数签名
def example_func(name: str, age: int = 18) -> str:
return f"{name}: {age}"
sig = inspect.signature(example_func)
print(sig) # (name: str, age: int = 18) -> str
for param_name, param in sig.parameters.items():
print(f"{param_name}: {param.annotation}, default={param.default}")
# 获取类成员
members = inspect.getmembers(User)
for name, value in members:
print(f"{name}: {type(value)}")
# 7.2、动态属性操作
class Config:
host = "localhost"
port = 8080
# getattr - 获取属性
print(getattr(Config, 'host')) # localhost
print(getattr(Config, 'timeout', 30)) # 30 (默认值)
# setattr - 设置属性
setattr(Config, 'host', '192.168.1.1')
print(Config.host) # 192.168.1.1
# hasattr - 检查属性是否存在
print(hasattr(Config, 'host')) # True
print(hasattr(Config, 'timeout')) # False
# delattr - 删除属性
delattr(Config, 'port')
print(hasattr(Config, 'port')) # False
# 实际应用:动态配置加载
config_data = {
'database_host': 'localhost',
'database_port': 3306,
'cache_enabled': True
}
class AppConfig:
pass
for key, value in config_data.items():
setattr(AppConfig, key, value)
print(AppConfig.database_host) # localhost
# 7.3、动态导入模块
import importlib
# 动态导入模块
math_module = importlib.import_module('math')
print(math_module.sqrt(16)) # 4.0
# 动态导入并获取属性
module = importlib.import_module('json')
dumps_func = getattr(module, 'dumps')
print(dumps_func({'name': 'Alice'})) # {"name": "Alice"}
# __import__() 函数
os_module = __import__('os')
print(os_module.getcwd())
# 实际应用:插件系统
def load_plugin(plugin_name: str):
"""动态加载插件"""
try:
module = importlib.import_module(f'plugins.{plugin_name}')
if hasattr(module, 'Plugin'):
return module.Plugin()
else:
raise AttributeError(f"Plugin class not found in {plugin_name}")
except ImportError as e:
print(f"Failed to load plugin: {e}")
return None
# 7.4、类型检查
# type() vs isinstance()
value = [1, 2, 3]
print(type(value)) # <class 'list'>
print(type(value) == list) # True
# isinstance() - 推荐使用,支持继承
print(isinstance(value, list)) # True
print(isinstance(value, (list, tuple))) # True - 检查多个类型
# issubclass() - 判断继承关系
class Animal:
pass
class Dog(Animal):
pass
print(issubclass(Dog, Animal)) # True
print(issubclass(Dog, object)) # True - 所有类都继承自object
# 鸭子类型应用
def process_iterable(obj):
"""处理任何可迭代对象"""
if hasattr(obj, '__iter__'):
for item in obj:
print(item)
else:
print("Not iterable")
process_iterable([1, 2, 3]) # 处理列表
process_iterable("hello") # 处理字符串
process_iterable(range(5)) # 处理range对象
# 7.5、动态创建类
# 使用type()创建类
# type(name, bases, dict)
User = type('User', (object,), {
'name': 'Alice',
'greet': lambda self: f"Hello, {self.name}"
})
user = User()
print(user.name) # Alice
print(user.greet()) # Hello, Alice
# 更复杂的示例
def __init__(self, name, age):
self.name = name
self.age = age
def get_info(self):
return f"{self.name}, {self.age} years old"
Person = type('Person', (object,), {
'__init__': __init__,
'get_info': get_info
})
p = Person("Bob", 25)
print(p.get_info()) # Bob, 25 years old
# 实际应用:ORM模型动态生成
def create_model(table_name: str, fields: dict):
"""动态创建数据库模型"""
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
def __repr__(self):
field_strs = [f"{k}={getattr(self, k)}" for k in fields.keys()]
return f"{table_name}({', '.join(field_strs)})"
attrs = {
'__init__': __init__,
'__repr__': __repr__,
'_table_name': table_name,
'_fields': fields
}
return type(table_name, (object,), attrs)
# 创建User模型
UserModel = create_model('users', {
'id': 'INTEGER',
'name': 'VARCHAR(100)',
'email': 'VARCHAR(100)'
})
user = UserModel(id=1, name='Alice', email='alice@example.com')
print(user) # users(id=1, name=Alice, email=alice@example.com)
# 7.6、元类(Metaclass)基础
# 元类是创建类的类
class Meta(type):
"""自定义元类"""
def __new__(mcs, name, bases, attrs):
# 在类创建时执行
print(f"Creating class: {name}")
# 添加类属性
attrs['created_by'] = 'Meta'
return super().__new__(mcs, name, bases, attrs)
# 使用元类
class MyClass(metaclass=Meta):
pass
# 输出: Creating class: MyClass
print(MyClass.created_by) # Meta
# 实际应用:自动注册类
_registry = {}
class RegisterMeta(type):
"""自动注册元类"""
def __new__(mcs, name, bases, attrs):
cls = super().__new__(mcs, name, bases, attrs)
if name != 'Base': # 跳过基类
_registry[name] = cls
return cls
class Base(metaclass=RegisterMeta):
pass
class PluginA(Base):
pass
class PluginB(Base):
pass
print(_registry) # {'PluginA': <class '__main__.PluginA'>, 'PluginB': <class '__main__.PluginB'>}
# 7.7、实战案例
案例1:简易依赖注入
class Container:
"""依赖注入容器"""
def __init__(self):
self._services = {}
def register(self, interface, implementation):
"""注册服务"""
self._services[interface] = implementation
def resolve(self, interface):
"""解析服务"""
implementation = self._services.get(interface)
if implementation is None:
raise ValueError(f"Service {interface} not registered")
# 检查是否需要依赖注入
sig = inspect.signature(implementation.__init__)
dependencies = {}
for param_name, param in sig.parameters.items():
if param_name == 'self':
continue
if param.annotation != inspect.Parameter.empty:
dependencies[param_name] = self.resolve(param.annotation)
return implementation(**dependencies)
# 使用示例
class Database:
def query(self, sql):
return f"Executing: {sql}"
class UserRepository:
def __init__(self, db: Database):
self.db = db
def find_all(self):
return self.db.query("SELECT * FROM users")
# 注册服务
container = Container()
container.register(Database, Database)
container.register(UserRepository, UserRepository)
# 解析服务(自动注入依赖)
repo = container.resolve(UserRepository)
print(repo.find_all()) # Executing: SELECT * FROM users
案例2:序列化/反序列化
import json
from typing import get_type_hints
class Serializable:
"""可序列化基类"""
def to_dict(self):
"""转换为字典"""
result = {}
for key, value in self.__dict__.items():
if isinstance(value, Serializable):
result[key] = value.to_dict()
else:
result[key] = value
return result
@classmethod
def from_dict(cls, data: dict):
"""从字典创建对象"""
hints = get_type_hints(cls.__init__)
kwargs = {}
for key, value in data.items():
if key in hints:
hint = hints[key]
# 如果是Serializable子类,递归创建
if isinstance(hint, type) and issubclass(hint, Serializable):
kwargs[key] = hint.from_dict(value)
else:
kwargs[key] = value
else:
kwargs[key] = value
return cls(**kwargs)
class Address(Serializable):
def __init__(self, city: str, street: str):
self.city = city
self.street = street
class User(Serializable):
def __init__(self, name: str, age: int, address: Address):
self.name = name
self.age = age
self.address = address
# 使用
user = User("Alice", 25, Address("Beijing", "Main St"))
user_dict = user.to_dict()
print(json.dumps(user_dict, indent=2))
# 反序列化
restored = User.from_dict(user_dict)
print(f"{restored.name} lives in {restored.address.city}")
对比总结:
| 特性 | Python | Java |
|---|---|---|
| 反射API | inspect, getattr, setattr | java.lang.reflect |
| 获取类信息 | inspect.getmembers() | Class.getDeclaredMethods() |
| 动态调用 | getattr(obj, 'method')() | method.invoke(obj) |
| 动态创建类 | type(name, bases, dict) | Proxy.newProxyInstance() |
| 类型检查 | isinstance(), issubclass() | instanceof, isAssignableFrom() |
| 元类 | class Meta(type) | 不支持(使用注解处理器) |
| 灵活性 | 极高 | 中等 |
| 性能 | 较慢 | 较快 |
# 8、Python标准库精选
Python的标准库非常丰富,开箱即用。作为Java开发者,你会发现很多功能在Python标准库中已经实现,无需引入第三方依赖。
# 8.1、常用内置模块
# a、os与sys模块
os模块 - 操作系统交互
import os
# 获取当前工作目录
print(os.getcwd()) # D:\workspace\project
# 改变工作目录
os.chdir('/tmp')
# 列出目录内容
files = os.listdir('.')
print(files)
# 创建目录
os.mkdir('new_folder')
os.makedirs('path/to/folder', exist_ok=True) # 递归创建
# 删除文件和目录
os.remove('file.txt') # 删除文件
os.rmdir('folder') # 删除空目录
import shutil
shutil.rmtree('folder') # 删除非空目录
# 文件和目录判断
print(os.path.exists('file.txt')) # 是否存在
print(os.path.isfile('file.txt')) # 是否是文件
print(os.path.isdir('folder')) # 是否是目录
# 路径操作
full_path = os.path.join('path', 'to', 'file.txt') # path/to/file.txt
dirname = os.path.dirname('/path/to/file.txt') # /path/to
basename = os.path.basename('/path/to/file.txt') # file.txt
name, ext = os.path.splitext('file.txt') # ('file', '.txt')
# 环境变量
print(os.environ.get('PATH'))
os.environ['MY_VAR'] = 'value'
# 执行系统命令
os.system('ls -l') # 不推荐,使用subprocess模块
sys模块 - Python解释器交互
import sys
# 命令行参数
print(sys.argv) # ['script.py', 'arg1', 'arg2']
# Python版本信息
print(sys.version)
print(sys.version_info) # sys.version_info(major=3, minor=9, ...)
# 模块搜索路径
print(sys.path)
sys.path.append('/custom/path')
# 退出程序
sys.exit(0) # 正常退出
sys.exit(1) # 异常退出
# 标准输入输出
sys.stdout.write("Hello\n")
line = sys.stdin.readline()
# 获取对象大小
numbers = [1, 2, 3, 4, 5]
print(sys.getsizeof(numbers)) # 字节数
Java对比:
// Java获取当前目录
String currentDir = System.getProperty("user.dir");
// Java环境变量
String path = System.getenv("PATH");
// Java命令行参数
public static void main(String[] args) {
// args数组
}
# b、datetime模块
from datetime import datetime, date, time, timedelta
# 获取当前时间
now = datetime.now()
print(now) # 2025-01-26 15:30:45.123456
today = date.today()
print(today) # 2025-01-26
# 创建日期时间
dt = datetime(2025, 1, 26, 15, 30, 45)
d = date(2025, 1, 26)
t = time(15, 30, 45)
# 格式化输出
print(now.strftime('%Y-%m-%d %H:%M:%S')) # 2025-01-26 15:30:45
print(now.strftime('%Y年%m月%d日')) # 2025年01月26日
# 解析字符串
dt = datetime.strptime('2025-01-26 15:30:45', '%Y-%m-%d %H:%M:%S')
# 日期运算
tomorrow = today + timedelta(days=1)
next_week = today + timedelta(weeks=1)
one_hour_later = now + timedelta(hours=1)
# 时间差
diff = datetime(2025, 12, 31) - now
print(diff.days) # 剩余天数
print(diff.total_seconds()) # 总秒数
# 时间戳
timestamp = now.timestamp() # 转为时间戳
dt = datetime.fromtimestamp(timestamp) # 从时间戳创建
# Java对比
"""
// Java 8+
LocalDateTime now = LocalDateTime.now();
LocalDate today = LocalDate.now();
// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = now.format(formatter);
// 日期运算
LocalDate tomorrow = today.plusDays(1);
"""
# c、json模块
import json
# Python对象转JSON字符串
data = {
'name': 'Alice',
'age': 25,
'skills': ['Python', 'Java'],
'active': True
}
json_str = json.dumps(data)
print(json_str) # {"name": "Alice", "age": 25, ...}
# 美化输出
json_str = json.dumps(data, indent=2, ensure_ascii=False)
print(json_str)
# JSON字符串转Python对象
data = json.loads(json_str)
print(data['name']) # Alice
# 读写JSON文件
# 写入
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# 读取
with open('data.json', 'r', encoding='utf-8') as f:
data = json.load(f)
# 自定义JSON编码
from datetime import datetime
class DateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
data = {'created': datetime.now()}
json_str = json.dumps(data, cls=DateTimeEncoder)
# Java对比
"""
// Java使用Jackson或Gson
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(data);
Data data = mapper.readValue(json, Data.class);
"""
# d、re模块(正则表达式)
import re
# 基本匹配
text = "My email is alice@example.com"
match = re.search(r'\w+@\w+\.\w+', text)
if match:
print(match.group()) # alice@example.com
# 查找所有匹配
text = "Phone: 123-456-7890, Mobile: 098-765-4321"
phones = re.findall(r'\d{3}-\d{3}-\d{4}', text)
print(phones) # ['123-456-7890', '098-765-4321']
# 替换
text = "Hello World"
result = re.sub(r'World', 'Python', text)
print(result) # Hello Python
# 分割
text = "apple,banana;orange:grape"
fruits = re.split(r'[,;:]', text)
print(fruits) # ['apple', 'banana', 'orange', 'grape']
# 编译正则(提高性能)
pattern = re.compile(r'\d+')
numbers = pattern.findall("I have 3 apples and 5 oranges")
print(numbers) # ['3', '5']
# 捕获组
text = "Name: Alice, Age: 25"
match = re.search(r'Name: (\w+), Age: (\d+)', text)
if match:
print(match.group(1)) # Alice
print(match.group(2)) # 25
print(match.groups()) # ('Alice', '25')
# 常用正则表达式
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
url_pattern = r'^https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b'
phone_pattern = r'^\d{3}-\d{3}-\d{4}$'
# e、math与random模块
import math
import random
# math模块
print(math.pi) # 3.141592653589793
print(math.e) # 2.718281828459045
print(math.sqrt(16)) # 4.0
print(math.pow(2, 3)) # 8.0
print(math.ceil(4.3)) # 5
print(math.floor(4.7)) # 4
print(math.fabs(-5)) # 5.0
# 三角函数
print(math.sin(math.pi / 2)) # 1.0
print(math.cos(0)) # 1.0
# random模块
print(random.random()) # 0.0到1.0的随机浮点数
# 随机整数
print(random.randint(1, 10)) # 1到10的随机整数(包含10)
print(random.randrange(1, 10)) # 1到9的随机整数(不含10)
# 随机选择
colors = ['red', 'green', 'blue']
print(random.choice(colors)) # 随机选择一个
# 随机多个(有放回)
print(random.choices(colors, k=3)) # ['red', 'blue', 'red']
# 随机多个(无放回)
print(random.sample(colors, k=2)) # ['green', 'red']
# 打乱列表
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)
print(numbers) # [3, 1, 5, 2, 4]
# 设置随机种子(可复现)
random.seed(42)
print(random.random()) # 相同种子产生相同序列
# f、collections模块
from collections import Counter, defaultdict, deque, namedtuple, OrderedDict
# Counter - 计数器
words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counter = Counter(words)
print(counter) # Counter({'apple': 3, 'banana': 2, 'orange': 1})
print(counter.most_common(2)) # [('apple', 3), ('banana', 2)]
# defaultdict - 默认值字典
dd = defaultdict(list)
dd['fruits'].append('apple') # 不需要检查key是否存在
print(dd) # defaultdict(<class 'list'>, {'fruits': ['apple']})
# 按类别分组
data = [('fruit', 'apple'), ('veg', 'carrot'), ('fruit', 'banana')]
grouped = defaultdict(list)
for category, item in data:
grouped[category].append(item)
print(dict(grouped)) # {'fruit': ['apple', 'banana'], 'veg': ['carrot']}
# deque - 双端队列
dq = deque([1, 2, 3])
dq.append(4) # 右端添加
dq.appendleft(0) # 左端添加
dq.pop() # 右端移除
dq.popleft() # 左端移除
print(dq) # deque([1, 2, 3])
# 限制长度的deque(用作循环缓冲区)
buffer = deque(maxlen=3)
for i in range(5):
buffer.append(i)
print(buffer) # deque([2, 3, 4], maxlen=3)
# namedtuple - 命名元组
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y) # 10 20
print(p[0], p[1]) # 也可以用索引
# OrderedDict - 有序字典(Python 3.7+普通dict也保序)
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
print(list(od.keys())) # ['a', 'b', 'c']
# Java对比
"""
// Java Counter类似
Map<String, Integer> counter = new HashMap<>();
// 需要手动计数
// Java deque
Deque<Integer> deque = new ArrayDeque<>();
deque.addFirst(1);
deque.addLast(2);
"""
# g、pathlib模块
from pathlib import Path
# 创建路径对象
p = Path('/usr/local/bin')
p = Path.home() # 用户主目录
p = Path.cwd() # 当前工作目录
# 路径拼接
config_path = Path.home() / '.config' / 'app' / 'settings.json'
print(config_path) # /home/user/.config/app/settings.json
# 路径属性
p = Path('/path/to/file.txt')
print(p.name) # file.txt
print(p.stem) # file
print(p.suffix) # .txt
print(p.parent) # /path/to
print(p.parts) # ('/', 'path', 'to', 'file.txt')
# 文件操作
p = Path('test.txt')
p.write_text('Hello World', encoding='utf-8') # 写入
content = p.read_text(encoding='utf-8') # 读取
p.unlink() # 删除文件
# 目录操作
dir_path = Path('new_folder')
dir_path.mkdir(exist_ok=True) # 创建目录
dir_path.mkdir(parents=True, exist_ok=True) # 递归创建
# 判断
p = Path('file.txt')
print(p.exists()) # 是否存在
print(p.is_file()) # 是否是文件
print(p.is_dir()) # 是否是目录
# 遍历目录
for file in Path('.').iterdir():
print(file)
# 递归查找
for py_file in Path('.').rglob('*.py'):
print(py_file)
# 对比os.path
"""
# 旧方式
import os
path = os.path.join(os.path.expanduser('~'), '.config', 'app')
# 新方式(pathlib)
path = Path.home() / '.config' / 'app'
"""
模块对比总结:
| 功能 | Python模块 | Java对应 |
|---|---|---|
| 操作系统交互 | os | System, Runtime |
| 文件路径 | pathlib, os.path | java.nio.file.Path |
| 日期时间 | datetime | java.time.* |
| JSON处理 | json | Jackson, Gson |
| 正则表达式 | re | java.util.regex.Pattern |
| 随机数 | random | java.util.Random |
| 高级集合 | collections | java.util.* |
# 8.2、数据处理
# a、csv模块
import csv
# 读取CSV文件
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f)
headers = next(reader) # 读取表头
for row in reader:
print(row) # ['Alice', '25', 'Beijing']
# 使用DictReader(推荐)
with open('data.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
print(row['name'], row['age']) # 按列名访问
# 写入CSV文件
data = [
['name', 'age', 'city'],
['Alice', 25, 'Beijing'],
['Bob', 30, 'Shanghai']
]
with open('output.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerows(data)
# 使用DictWriter
with open('output.csv', 'w', encoding='utf-8', newline='') as f:
fieldnames = ['name', 'age', 'city']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'name': 'Alice', 'age': 25, 'city': 'Beijing'})
# b、XML处理
import xml.etree.ElementTree as ET
# 解析XML
xml_str = '''
<users>
<user id="1">
<name>Alice</name>
<age>25</age>
</user>
<user id="2">
<name>Bob</name>
<age>30</age>
</user>
</users>
'''
root = ET.fromstring(xml_str)
# 遍历元素
for user in root.findall('user'):
user_id = user.get('id')
name = user.find('name').text
age = user.find('age').text
print(f"ID: {user_id}, Name: {name}, Age: {age}")
# 创建XML
root = ET.Element('users')
user = ET.SubElement(root, 'user', id='1')
ET.SubElement(user, 'name').text = 'Alice'
ET.SubElement(user, 'age').text = '25'
# 保存XML
tree = ET.ElementTree(root)
tree.write('users.xml', encoding='utf-8', xml_declaration=True)
# c、pickle模块(序列化)
import pickle
# Python对象序列化
data = {
'name': 'Alice',
'scores': [85, 90, 88],
'metadata': {'created': '2025-01-26'}
}
# 保存到文件
with open('data.pkl', 'wb') as f:
pickle.dump(data, f)
# 从文件加载
with open('data.pkl', 'rb') as f:
loaded_data = pickle.load(f)
print(loaded_data)
# 序列化为字节串
bytes_data = pickle.dumps(data)
restored = pickle.loads(bytes_data)
# d、struct模块(二进制数据)
import struct
# 打包二进制数据
# 格式: i=int, f=float, s=string
packed = struct.pack('i f 10s', 42, 3.14, b'Hello')
print(packed) # b'*\x00\x00\x00\xc3\xf5H@Hello\x00\x00\x00\x00\x00'
# 解包二进制数据
unpacked = struct.unpack('i f 10s', packed)
print(unpacked) # (42, 3.140000104904175, b'Hello\x00\x00\x00\x00\x00')
# 实际应用:读取二进制文件
with open('data.bin', 'rb') as f:
data = f.read(struct.calcsize('i f'))
values = struct.unpack('i f', data)
# 8.3、网络编程
# a、socket模块
import socket
# TCP服务器
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8080))
server.listen(5)
print("Server listening on port 8080...")
while True:
client, addr = server.accept()
print(f"Connection from {addr}")
data = client.recv(1024)
client.send(b"Hello from server")
client.close()
# TCP客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 8080))
client.send(b"Hello server")
response = client.recv(1024)
print(response.decode())
client.close()
# b、urllib模块
from urllib import request, parse
# GET请求
response = request.urlopen('https://api.github.com')
html = response.read().decode('utf-8')
print(html)
# POST请求
data = parse.urlencode({'key': 'value'}).encode()
req = request.Request('https://httpbin.org/post', data=data)
response = request.urlopen(req)
print(response.read().decode())
# 设置请求头
req = request.Request('https://api.github.com')
req.add_header('User-Agent', 'Python App')
response = request.urlopen(req)
# c、http.server
# 命令行启动简单HTTP服务器
# python -m http.server 8000
# 自定义HTTP服务器
from http.server import HTTPServer, BaseHTTPRequestHandler
class MyHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(b'<h1>Hello World</h1>')
server = HTTPServer(('localhost', 8000), MyHandler)
server.serve_forever()
# 8.4、多线程与多进程
# a、threading模块
import threading
import time
# 创建线程
def worker(name):
print(f"Thread {name} starting")
time.sleep(2)
print(f"Thread {name} done")
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
# 线程类
class MyThread(threading.Thread):
def __init__(self, name):
super().__init__()
self.name = name
def run(self):
print(f"{self.name} is running")
# 线程锁
lock = threading.Lock()
def safe_increment():
global counter
with lock:
counter += 1
# b、multiprocessing模块
from multiprocessing import Process, Pool, Queue
# 创建进程
def worker(name):
print(f"Process {name} starting")
if __name__ == '__main__':
processes = []
for i in range(5):
p = Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
# 进程池
def square(x):
return x ** 2
if __name__ == '__main__':
with Pool(processes=4) as pool:
results = pool.map(square, range(10))
print(results)
# c、concurrent.futures模块
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
def task(n):
time.sleep(1)
return n ** 2
# 线程池
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(task, i) for i in range(10)]
for future in futures:
print(future.result())
# 进程池
with ProcessPoolExecutor(max_workers=4) as executor:
results = executor.map(task, range(10))
print(list(results))
# d、GIL详解
GIL(全局解释器锁)是CPython的实现细节:
影响:
- 同一时刻只有一个线程执行Python字节码
- 多线程无法利用多核CPU进行CPU密集型任务
- I/O密集型任务不受影响
解决方案:
- CPU密集型: 使用multiprocessing
- I/O密集型: 使用threading或asyncio
- 混合型: 根据具体情况选择
Java对比:
- Java没有GIL,多线程可以真正并行
- Python的多线程更适合I/O操作
- Python的多进程类似Java的多线程
# 8.5、日志与调试
# a、logging模块
import logging
# 基本配置
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
filename='app.log'
)
# 使用日志
logging.debug("Debug message")
logging.info("Info message")
logging.warning("Warning message")
logging.error("Error message")
logging.critical("Critical message")
# 创建logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# 添加处理器
file_handler = logging.FileHandler('app.log')
console_handler = logging.StreamHandler()
# 设置格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 使用logger
logger.info("Application started")
# b、pdb调试器
import pdb
def buggy_function(x, y):
result = x + y
pdb.set_trace() # 设置断点
result = result * 2
return result
# 调试命令:
# n - 下一行
# s - 进入函数
# c - 继续执行
# p variable - 打印变量
# l - 显示当前代码
# q - 退出调试
# c、traceback模块
import traceback
try:
result = 1 / 0
except Exception as e:
# 打印完整堆栈
traceback.print_exc()
# 获取堆栈信息
tb_str = traceback.format_exc()
print(tb_str)
# 记录到日志
logging.error("Error occurred", exc_info=True)
# 9、异步编程
异步编程是Python的重要特性,特别适合处理I/O密集型任务。对于Java开发者来说,Python的异步编程模型比Java的CompletableFuture更加直观和强大。
# 9.1、asyncio基础
# a、async/await语法
Python 3.5引入了async/await语法,让异步代码看起来像同步代码一样直观。
import asyncio
async def greet(name):
print(f"开始问候 {name}")
await asyncio.sleep(1) # 异步等待1秒
return f"Hello, {name}!"
async def main():
result = await greet("张三")
print(result)
# 运行异步程序
asyncio.run(main())
对比Java的异步编程:
// Java使用CompletableFuture
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello, 张三!";
});
future.thenAccept(System.out::println);
核心概念:
async def:定义协程函数await:暂停当前协程,等待另一个协程完成asyncio.run():启动事件循环并运行主协程
# b、协程概念
协程(Coroutine)是可以暂停和恢复执行的函数,比线程更轻量级。
import asyncio
import time
async def task(name, duration):
print(f"任务 {name} 开始")
await asyncio.sleep(duration)
print(f"任务 {name} 完成")
return f"{name} 结果"
async def main():
start_time = time.time()
# 顺序执行(总时间 = 3秒)
result1 = await task("A", 1)
result2 = await task("B", 2)
end_time = time.time()
print(f"顺序执行耗时: {end_time - start_time:.2f}秒")
asyncio.run(main())
协程 vs 线程对比:
| 特性 | 协程 | 线程 |
|---|---|---|
| 创建成本 | 极低(几KB) | 较高(几MB) |
| 切换成本 | 用户态切换 | 内核态切换 |
| 数量限制 | 可以创建数万个 | 通常数百个 |
| 适用场景 | I/O密集型 | CPU密集型 |
# c、事件循环
事件循环是异步编程的核心,负责调度和执行协程。
import asyncio
async def worker(name):
print(f"{name} 开始工作")
await asyncio.sleep(1)
print(f"{name} 完成工作")
async def main():
# 创建任务
task1 = asyncio.create_task(worker("任务1"))
task2 = asyncio.create_task(worker("任务2"))
# 等待所有任务完成
await task1
await task2
print("所有任务完成")
# 使用asyncio.run()
asyncio.run(main())
# 手动控制事件循环(高级用法)
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
asyncio.run() vs await的区别:
asyncio.run():启动新的事件循环,通常用于程序入口await:在现有事件循环中等待协程完成
import asyncio
async def nested():
return "嵌套协程结果"
async def outer():
# ✅ 正确:使用await
result = await nested()
print(result)
# ❌ 错误:不能在协程中使用asyncio.run()
# asyncio.run(nested()) # RuntimeError: no running event loop
asyncio.run(outer())
# d、Task与Future
Task是Future的子类,用于包装协程并调度执行。
import asyncio
async def background_task(name, delay):
print(f"后台任务 {name} 开始")
await asyncio.sleep(delay)
print(f"后台任务 {name} 完成")
return f"{name} 的结果"
async def main():
# 创建多个任务(立即开始执行)
tasks = [
asyncio.create_task(background_task("A", 2)),
asyncio.create_task(background_task("B", 1)),
asyncio.create_task(background_task("C", 3))
]
# 等待所有任务完成
results = await asyncio.gather(*tasks)
print("所有任务结果:", results)
# 带超时的等待
try:
await asyncio.wait_for(asyncio.create_task(background_task("D", 5)), timeout=3)
except asyncio.TimeoutError:
print("任务D超时")
asyncio.run(main())
Task的高级用法:
import asyncio
async def cancellable_task():
try:
for i in range(10):
print(f"工作中... {i}")
await asyncio.sleep(1)
except asyncio.CancelledError:
print("任务被取消")
# 清理资源
await asyncio.sleep(0.5)
print("清理完成")
raise
async def main():
task = asyncio.create_task(cancellable_task())
# 3秒后取消任务
await asyncio.sleep(3)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("主协程捕获到取消异常")
asyncio.run(main())
# 9.2、异步I/O
# a、异步文件操作
Python 3.4+提供了异步文件操作API。
import asyncio
import aiofiles # 第三方库,功能更完整
async def read_file_async(filename):
# 使用aiofiles(推荐)
async with aiofiles.open(filename, 'r', encoding='utf-8') as f:
content = await f.read()
print(f"文件内容长度: {len(content)}")
return content
async def write_file_async(filename, content):
async with aiofiles.open(filename, 'w', encoding='utf-8') as f:
await f.write(content)
print(f"写入文件: {filename}")
async def process_files():
# 并发处理多个文件
files = ['file1.txt', 'file2.txt', 'file3.txt']
tasks = []
for filename in files:
content = f"这是 {filename} 的内容\n"
tasks.append(write_file_async(filename, content))
await asyncio.gather(*tasks)
# 并发读取文件
read_tasks = [read_file_async(f) for f in files]
contents = await asyncio.gather(*read_tasks)
print(f"读取了 {len(contents)} 个文件")
# 安装aiofiles: pip install aiofiles
# asyncio.run(process_files())
对比Java的异步文件操作:
// Java NIO.2异步文件操作
AsynchronousFileChannel channel = AsynchronousFileChannel.open(
Paths.get("file.txt"), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> readResult = channel.read(buffer, 0);
// 处理结果
while (!readResult.isDone()) {
// 可以做其他工作
}
# b、异步网络请求
使用aiohttp库进行异步HTTP请求。
import asyncio
import aiohttp
import time
async def fetch_url(session, url):
try:
async with session.get(url) as response:
print(f"获取 {url} - 状态: {response.status}")
content = await response.text()
return {
'url': url,
'status': response.status,
'length': len(content),
'content': content[:100] # 只返回前100个字符
}
except Exception as e:
print(f"请求 {url} 失败: {e}")
return {'url': url, 'error': str(e)}
async def fetch_multiple_urls(urls):
# 创建会话(复用连接)
async with aiohttp.ClientSession() as session:
# 并发请求多个URL
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
# 处理结果
success_count = 0
for result in results:
if isinstance(result, dict) and 'error' not in result:
success_count += 1
print(f"✅ {result['url']}: {result['status']} ({result['length']} 字符)")
else:
print(f"❌ 失败: {result}")
print(f"成功请求: {success_count}/{len(urls)}")
return results
async def main():
urls = [
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/2',
'https://httpbin.org/status/200',
'https://httpbin.org/status/404'
]
start_time = time.time()
await fetch_multiple_urls(urls)
end_time = time.time()
print(f"总耗时: {end_time - start_time:.2f}秒")
# 安装aiohttp: pip install aiohttp
# asyncio.run(main())
异步HTTP客户端的最佳实践:
import asyncio
import aiohttp
from aiohttp import ClientTimeout, ClientSession
class AsyncHttpClient:
def __init__(self, timeout=30, max_connections=100):
self.timeout = ClientTimeout(total=timeout)
self.connector = aiohttp.TCPConnector(limit=max_connections)
async def __aenter__(self):
self.session = ClientSession(
timeout=self.timeout,
connector=self.connector
)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self.session.close()
async def get(self, url, **kwargs):
async with self.session.get(url, **kwargs) as response:
return await response.json()
async def post(self, url, data=None, json=None, **kwargs):
async with self.session.post(url, data=data, json=json, **kwargs) as response:
return await response.json()
async def api_client_example():
async with AsyncHttpClient() as client:
# GET请求
data = await client.get('https://api.github.com/users/python')
print(f"Python GitHub followers: {data['followers']}")
# POST请求
result = await client.post('https://httpbin.org/post', json={'key': 'value'})
print(f"POST响应: {result['json']}")
# asyncio.run(api_client_example())
# 9.3、异步编程最佳实践
# a、何时使用异步
适合异步的场景:
- I/O密集型操作(网络请求、数据库查询、文件读写)
- 需要并发处理大量连接
- 实时性要求高的应用
- WebSocket、聊天服务器、API网关
# ✅ 适合异步:网络爬虫
import asyncio
import aiohttp
async def crawl_urls(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
return results
# ✅ 适合异步:WebSocket聊天服务器
async def handle_chat_messages(websocket):
async for message in websocket:
await broadcast_to_other_clients(message)
# ❌ 不适合异步:CPU密集型计算
async def heavy_computation():
# 这会阻塞事件循环
result = sum(i * i for i in range(10000000))
return result
不适合异步的场景:
- CPU密集型计算(数值计算、图像处理)
- 简单的脚本程序
- 同步第三方库的包装
# b、异步与多线程的选择
异步 vs 多线程对比:
| 维度 | 异步 | 多线程 |
|---|---|---|
| 并发模型 | 单线程事件循环 | 多线程抢占式 |
| 内存占用 | 低(单线程) | 高(每个线程栈空间) |
| 上下文切换 | 快(用户态) | 慢(内核态) |
| 编程复杂度 | 高(需要异步思维) | 中等 |
| 调试难度 | 较高 | 中等 |
| 适用场景 | I/O密集型 | CPU密集型或阻塞操作 |
混合使用示例:
import asyncio
import concurrent.futures
import time
def cpu_intensive_task(n):
"""CPU密集型任务 - 在线程池中执行"""
return sum(i * i for i in range(n))
async def async_io_task():
"""I/O密集型任务 - 异步执行"""
await asyncio.sleep(1)
return "I/O任务完成"
async def mixed_workload():
start_time = time.time()
# 创建线程池执行器
loop = asyncio.get_event_loop()
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
# 并发执行不同类型的任务
tasks = [
# CPU密集型任务在线程池中
loop.run_in_executor(executor, cpu_intensive_task, 1000000),
loop.run_in_executor(executor, cpu_intensive_task, 2000000),
# I/O密集型任务异步执行
async_io_task(),
async_io_task(),
]
results = await asyncio.gather(*tasks)
end_time = time.time()
print(f"混合任务完成,耗时: {end_time - start_time:.2f}秒")
print(f"结果: {results}")
asyncio.run(mixed_workload())
# c、常见陷阱
陷阱1:在协程中调用阻塞函数
import asyncio
import time
# ❌ 错误:阻塞事件循环
async def bad_example():
time.sleep(2) # 阻塞2秒,整个事件循环被阻塞
return "完成"
# ✅ 正确:使用异步版本
async def good_example():
await asyncio.sleep(2) # 非阻塞等待
return "完成"
# ✅ 或者在线程池中执行阻塞操作
async def blocking_in_thread():
loop = asyncio.get_event_loop()
with concurrent.futures.ThreadPoolExecutor() as executor:
result = await loop.run_in_executor(executor, time.sleep, 2)
return "完成"
陷阱2:忘记await
import asyncio
async def forget_await():
# ❌ 错误:忘记await,协程不会执行
asyncio.sleep(1)
print("这可能不会按预期执行")
async def correct_usage():
# ✅ 正确:使用await
await asyncio.sleep(1)
print("1秒后执行")
陷阱3:异常处理不当
import asyncio
async def task_with_error():
await asyncio.sleep(0.1)
raise ValueError("任务失败")
async def bad_error_handling():
# ❌ 错误:异常被忽略
asyncio.create_task(task_with_error())
async def good_error_handling():
# ✅ 正确:处理异常
task = asyncio.create_task(task_with_error())
try:
await task
except ValueError as e:
print(f"捕获到异常: {e}")
# ✅ 使用gather处理异常
async def gather_with_exceptions():
tasks = [
asyncio.create_task(task_with_error()),
asyncio.create_task(asyncio.sleep(1))
]
results = await asyncio.gather(*tasks, return_exceptions=True)
for i, result in enumerate(results):
if isinstance(result, Exception):
print(f"任务 {i} 失败: {result}")
else:
print(f"任务 {i} 成功: {result}")
陷阱4:过度使用异步
# ❌ 过度设计:简单的同步任务不需要异步
async def over_engineered():
result = await async_add(2, 3)
return result
# ✅ 简单直接
def simple():
return 2 + 3
最佳实践总结:
- 保持异步函数纯净:异步函数中只调用异步函数
- 正确处理异常:使用try/except包装可能失败的操作
- 避免阻塞操作:将阻塞操作放到线程池中
- 合理并发数:控制同时进行的任务数量
- 使用连接池:复用数据库和HTTP连接
- 性能监控:使用工具监控异步程序性能
import asyncio
import aiohttp
from asyncio import Semaphore
async def rate_limited_fetch(session, url, semaphore):
"""带速率限制的请求"""
async with semaphore: # 限制并发数
async with session.get(url) as response:
return await response.text()
async def robust_fetch_all(urls, max_concurrent=10):
"""健壮的批量请求"""
semaphore = Semaphore(max_concurrent)
async with aiohttp.ClientSession() as session:
tasks = [
rate_limited_fetch(session, url, semaphore)
for url in urls
]
results = await asyncio.gather(*tasks, return_exceptions=True)
success_count = sum(1 for r in results if not isinstance(r, Exception))
print(f"成功: {success_count}/{len(urls)}")
return results
# 10、Python生态与热门库
Python拥有丰富的第三方库生态,这是其受欢迎的重要原因。作为Java开发者,你会发现Python的库生态更加开放和多样化。
# 10.1、数据科学
# a、- 数值计算基础
NumPy是Python科学计算的基础库,提供高性能的多维数组对象和相关工具。类似Java的数学库,但功能更强大。
import numpy as np
# 创建数组 - 类似Java的int[]数组,但功能更强
arr = np.array([1, 2, 3, 4, 5]) # 一维数组
matrix = np.array([[1, 2], [3, 4]]) # 二维数组(矩阵)
# 数组运算 - 对每个元素进行运算,无需循环
result = arr * 2 # 每个元素都乘以2: [2, 4, 6, 8, 10]
# Java需要循环: for(int i=0; i<arr.length; i++) arr[i] *= 2;
# 矩阵乘法 - dot表示点积/矩阵乘法
dot_product = np.dot(matrix, matrix)
# 结果: [[7, 10], [15, 22]]
# 计算过程: [1*1+2*3, 1*2+2*4; 3*1+4*3, 3*2+4*4]
# 常用统计函数
print(np.mean(arr)) # 平均值: (1+2+3+4+5)/5 = 3.0
print(np.sum(arr)) # 求和: 1+2+3+4+5 = 15
print(np.max(arr)) # 最大值: 5
print(np.min(arr)) # 最小值: 1
print(np.std(arr)) # 标准差: 衡量数据分散程度
# 数组形状操作
print(matrix.shape) # 输出: (2, 2) 表示2行2列
reshaped = arr.reshape(5, 1) # 重塑为5行1列
# b、- 数据分析
Pandas是数据分析的核心库,提供DataFrame等数据结构,类似于Excel或数据库表的操作。
import pandas as pd
# 创建DataFrame - 类似数据库表或Excel表格
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'], # 姓名列
'age': [25, 30, 35], # 年龄列
'city': ['Beijing', 'Shanghai', 'Guangzhou'] # 城市列
})
# 数据查看操作
print(df.head()) # 查看前5行数据(默认5行)
print(df.tail(2)) # 查看后2行数据
print(df.info()) # 查看数据结构信息(列名、数据类型、非空值数量)
print(df.describe()) # 统计描述(count、mean、std、min、max等)
# 数据筛选 - 类似SQL的WHERE子句
filtered = df[df['age'] > 25] # 筛选年龄大于25的记录
adults = df[df['age'] >= 30] # 筛选年龄大于等于30的记录
# 列操作
print(df['name']) # 获取单列
print(df[['name', 'age']]) # 获取多列
df['salary'] = [8000, 10000, 12000] # 添加新列
# 文件读写 - 支持多种格式
df.to_csv('data.csv', index=False) # 保存为CSV文件,不保存行索引
df = pd.read_csv('data.csv') # 从CSV文件读取数据
# 也支持: read_excel(), read_json(), read_sql() 等
# c、- 数据可视化
Matplotlib是Python的2D绘图库,用于创建各种静态、动态和交互式图表。
import matplotlib.pyplot as plt
# 准备数据
x = [1, 2, 3, 4, 5] # X轴数据点
y = [2, 4, 6, 8, 10] # Y轴数据点
# 创建折线图
plt.figure(figsize=(8, 6)) # 设置图形大小(宽8英寸,高6英寸)
plt.plot(x, y, marker='o', linestyle='-', color='blue') # 绘制带圆点标记的蓝色线
plt.xlabel('X轴标签') # 设置X轴标签
plt.ylabel('Y轴标签') # 设置Y轴标签
plt.title('简单折线图') # 设置图表标题
plt.grid(True) # 显示网格线
plt.show() # 显示图表
# 创建柱状图
categories = ['A', 'B', 'C'] # 类别名称
values = [10, 20, 15] # 对应的值
plt.figure(figsize=(6, 4))
plt.bar(categories, values, color=['red', 'green', 'blue']) # 不同颜色的柱子
plt.ylabel('数值')
plt.title('柱状图示例')
plt.show()
# 创建子图 - 在一个窗口显示多个图表
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4)) # 1行2列的子图
ax1.plot(x, y) # 第一个子图
ax1.set_title('折线图')
ax2.bar(categories, values) # 第二个子图
ax2.set_title('柱状图')
plt.tight_layout() # 自动调整子图间距
plt.show()
# d、- 科学计算
SciPy构建在NumPy之上,提供更多科学计算功能,包括统计、优化、信号处理等。
from scipy import stats, optimize
import numpy as np
# 统计分析
data = [1, 2, 3, 4, 5, 3, 4, 2, 3, 4]
# 正态分布拟合 - 估计数据的正态分布参数
mean, std = stats.norm.fit(data) # 拟合正态分布,得到均值和标准差
print(f"估计均值: {mean:.2f}, 估计标准差: {std:.2f}")
# 假设检验 - 检验数据是否符合正态分布
statistic, p_value = stats.normaltest(data) # 正态性检验
print(f"正态检验p值: {p_value:.4f}") # p值>0.05通常认为符合正态分布
# 数值优化 - 寻找函数的最小值
def objective_function(x):
"""目标函数: y = x² + 2x + 1"""
return x**2 + 2*x + 1
# 寻找函数最小值点
result = optimize.minimize(objective_function, x0=0) # x0=0是初始猜测值
print(f"最小值点: x = {result.x[0]:.4f}") # 理论值应该是x=-1
print(f"最小值: y = {result.fun:.4f}") # 对应的函数值
# 求解方程 - 找到函数的零点
def equation(x):
"""方程: x² - 4 = 0"""
return x**2 - 4
# 寻找方程的根
root = optimize.fsolve(equation, 1)[0] # 从x=1开始搜索
print(f"方程x²-4=0的解: x = {root:.4f}") # 应该得到x=2
# 10.2、Web开发框架
# a、- 轻量级框架
Flask是一个轻量级的Web框架,简单灵活,适合小到中型项目。类似Spring Boot但更简洁。
from flask import Flask, request, jsonify
# 创建Flask应用实例 - 类似Spring Boot的@SpringBootApplication
app = Flask(__name__)
# 定义路由 - 类似Spring的@RequestMapping
@app.route('/') # 处理根路径 "/"
def hello():
"""处理GET请求到根路径"""
return 'Hello World!' # 直接返回字符串
# 支持多种HTTP方法的路由
@app.route('/api/users', methods=['GET', 'POST']) # 指定支持的HTTP方法
def users():
"""用户API端点 - 根据请求方法执行不同逻辑"""
if request.method == 'GET':
# 处理GET请求 - 获取用户列表
return jsonify({'users': []}) # jsonify()将字典转为JSON响应
elif request.method == 'POST':
# 处理POST请求 - 创建新用户
user_data = request.get_json() # 获取请求体中的JSON数据
# 这里可以添加用户创建逻辑
return jsonify({'status': 'created', 'user': user_data})
# 带参数的路由 - 类似Spring的@PathVariable
@app.route('/users/<int:user_id>') # <int:user_id>表示整数类型的路径参数
def get_user(user_id):
"""根据用户ID获取用户信息"""
return jsonify({'id': user_id, 'name': f'User {user_id}'})
# 查询参数示例
@app.route('/search')
def search():
"""处理查询参数 - 类似Spring的@RequestParam"""
query = request.args.get('q', '') # 获取查询参数q,默认空字符串
page = request.args.get('page', 1, type=int) # 获取page参数,默认1,转为int
return jsonify({'query': query, 'page': page})
# 启动开发服务器
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000) # debug=True启用调试模式
# 访问 http://localhost:5000 查看效果
# b、- 全栈框架
Django是一个功能完整的Web框架,内置ORM、管理后台、用户认证等,适合大型项目。更类似Spring全家桶。
# Django项目结构类似Spring Boot:
# myproject/
# ├── manage.py # 项目管理脚本(类似mvn spring-boot:run)
# ├── myproject/
# │ ├── settings.py # 项目配置(类似application.properties)
# │ ├── urls.py # 全局URL配置
# │ └── wsgi.py # WSGI配置
# └── myapp/
# ├── models.py # 数据模型(类似JPA Entity)
# ├── views.py # 视图函数(类似Controller)
# ├── urls.py # 应用URL配置
# └── admin.py # 管理后台配置
# models.py - 定义数据模型
from django.db import models
class User(models.Model):
"""用户模型 - 类似JPA的@Entity"""
# Django自动创建id主键,无需手动定义
name = models.CharField(max_length=100) # VARCHAR(100)字段
email = models.EmailField() # EMAIL字段,自动验证格式
created_at = models.DateTimeField(auto_now_add=True) # 创建时自动设置
updated_at = models.DateTimeField(auto_now=True) # 更新时自动设置
is_active = models.BooleanField(default=True) # 布尔字段,默认值
class Meta:
db_table = 'users' # 指定表名
ordering = ['-created_at'] # 默认按创建时间倒序
def __str__(self):
"""字符串表示 - 类似Java的toString()"""
return self.name
# views.py - 视图函数
from django.http import JsonResponse
from django.shortcuts import get_object_or_404 # 404处理
from django.views.decorators.csrf import csrf_exempt # CSRF保护
import json
def user_list(request):
"""用户列表API - 类似Spring的@GetMapping"""
if request.method == 'GET':
users = User.objects.all() # 查询所有用户,类似JPA的findAll()
data = {
'users': list(users.values('id', 'name', 'email')) # 转为字典列表
}
return JsonResponse(data)
@csrf_exempt # 禁用CSRF检查(仅用于API)
def user_create(request):
"""创建用户API - 类似Spring的@PostMapping"""
if request.method == 'POST':
data = json.loads(request.body) # 解析JSON请求体
user = User.objects.create( # 创建并保存用户
name=data['name'],
email=data['email']
)
return JsonResponse({'id': user.id, 'status': 'created'})
def user_detail(request, user_id):
"""用户详情API - 类似Spring的@PathVariable"""
user = get_object_or_404(User, id=user_id) # 查询用户,不存在则返回404
return JsonResponse({
'id': user.id,
'name': user.name,
'email': user.email,
'created_at': user.created_at.isoformat()
})
# urls.py - URL配置
from django.urls import path
from . import views
urlpatterns = [
path('users/', views.user_list, name='user_list'), # GET /users/
path('users/create/', views.user_create, name='user_create'), # POST /users/create/
path('users/<int:user_id>/', views.user_detail, name='user_detail'), # GET /users/1/
]
# c、- 现代异步框架
FastAPI是现代的异步Web框架,自动生成API文档,性能优异,类似Spring WebFlux。
from fastapi import FastAPI, HTTPException, Query, Path
from pydantic import BaseModel, EmailStr # 数据验证库
from typing import Optional, List
import asyncio
# 创建FastAPI应用
app = FastAPI(
title="用户管理API", # API文档标题
description="用户管理系统", # API描述
version="1.0.0" # 版本号
)
# 定义数据模型 - 类似Spring的DTO,自动验证
class User(BaseModel):
"""用户数据模型"""
name: str # 必填字符串字段
age: int # 必填整数字段
email: EmailStr # 邮箱格式验证
is_active: bool = True # 可选字段,默认True
class UserResponse(BaseModel):
"""用户响应模型"""
id: int
name: str
age: int
email: str
is_active: bool
# 模拟数据库
fake_db = []
next_id = 1
# 根路径 - 异步函数
@app.get("/")
async def root():
"""根路径处理器"""
return {"message": "欢迎使用用户管理API"}
# 创建用户 - 自动JSON解析和验证
@app.post("/users/", response_model=UserResponse, status_code=201)
async def create_user(user: User):
"""
创建新用户
- **name**: 用户姓名
- **age**: 用户年龄
- **email**: 用户邮箱
- **is_active**: 是否激活(可选)
"""
global next_id
user_dict = user.dict() # 转为字典
user_dict["id"] = next_id
fake_db.append(user_dict)
next_id += 1
return user_dict
# 获取用户列表 - 支持分页和过滤
@app.get("/users/", response_model=List[UserResponse])
async def get_users(
skip: int = Query(0, ge=0, description="跳过记录数"), # 查询参数,>=0
limit: int = Query(10, ge=1, le=100, description="返回记录数"), # 限制1-100
active_only: Optional[bool] = Query(None, description="只返回激活用户")
):
"""获取用户列表,支持分页"""
users = fake_db[skip:skip + limit] # 模拟分页
if active_only is not None:
users = [u for u in users if u["is_active"] == active_only]
return users
# 获取单个用户 - 路径参数验证
@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(
user_id: int = Path(..., gt=0, description="用户ID,必须大于0") # 路径参数验证
):
"""根据ID获取用户"""
user = next((u for u in fake_db if u["id"] == user_id), None)
if not user:
raise HTTPException(status_code=404, detail="用户不存在") # 抛出HTTP异常
return user
# 更新用户 - 部分更新
@app.patch("/users/{user_id}", response_model=UserResponse)
async def update_user(
user_id: int,
user_update: dict # 接受任意字段的字典
):
"""更新用户信息"""
user = next((u for u in fake_db if u["id"] == user_id), None)
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
# 更新字段
for key, value in user_update.items():
if key in user:
user[key] = value
return user
# 删除用户
@app.delete("/users/{user_id}")
async def delete_user(user_id: int):
"""删除用户"""
global fake_db
fake_db = [u for u in fake_db if u["id"] != user_id]
return {"message": "用户已删除"}
# 异步操作示例
@app.get("/users/{user_id}/async-info")
async def get_user_async_info(user_id: int):
"""模拟异步数据获取"""
# 模拟异步数据库查询
await asyncio.sleep(0.1) # 模拟I/O延迟
# 模拟并发调用多个服务
tasks = [
asyncio.create_task(get_user_profile(user_id)),
asyncio.create_task(get_user_stats(user_id)),
asyncio.create_task(get_user_preferences(user_id))
]
profile, stats, preferences = await asyncio.gather(*tasks)
return {
"profile": profile,
"stats": stats,
"preferences": preferences
}
async def get_user_profile(user_id: int):
"""模拟获取用户资料"""
await asyncio.sleep(0.05)
return {"user_id": user_id, "bio": "用户简介"}
async def get_user_stats(user_id: int):
"""模拟获取用户统计"""
await asyncio.sleep(0.03)
return {"user_id": user_id, "login_count": 42}
async def get_user_preferences(user_id: int):
"""模拟获取用户偏好"""
await asyncio.sleep(0.02)
return {"user_id": user_id, "theme": "dark"}
# 启动应用后,访问以下URL:
# http://localhost:8000/docs - 自动生成的交互式API文档(Swagger UI)
# http://localhost:8000/redoc - 另一种风格的API文档
# 这些文档是自动生成的,包含所有端点、参数、响应模型等信息
# 运行命令: uvicorn main:app --reload
# 10.3、网络请求
# a、- HTTP客户端
requests是Python最流行的HTTP库,API简洁易用,比Java的HttpClient更友好。
import requests
# 基本GET请求 - 比Java HttpClient简单很多
response = requests.get('https://api.github.com/users/octocat')
print(f"状态码: {response.status_code}") # HTTP状态码,如200、404等
print(f"响应头: {response.headers}") # 响应头字典
data = response.json() # 自动解析JSON响应为Python字典
print(f"用户名: {data['name']}") # 访问JSON数据
# 带查询参数的GET请求
params = {'q': 'python', 'sort': 'stars', 'order': 'desc'}
response = requests.get('https://api.github.com/search/repositories', params=params)
# 实际请求URL: https://api.github.com/search/repositories?q=python&sort=stars&order=desc
# POST请求 - 发送JSON数据
user_data = {'name': 'Alice', 'email': 'alice@example.com'}
response = requests.post(
'https://httpbin.org/post',
json=user_data, # 自动设置Content-Type为application/json
timeout=30 # 30秒超时
)
print(response.json()) # 查看响应
# POST请求 - 发送表单数据
form_data = {'username': 'alice', 'password': 'secret'}
response = requests.post(
'https://httpbin.org/post',
data=form_data # 发送表单数据(application/x-www-form-urlencoded)
)
# 设置请求头 - 比如API认证
headers = {
'Authorization': 'Bearer your-token-here',
'User-Agent': 'MyApp/1.0',
'Accept': 'application/json'
}
response = requests.get('https://api.example.com/data', headers=headers)
# 会话管理 - 保持cookies和连接复用
session = requests.Session()
session.headers.update({'Authorization': 'Bearer token123'}) # 为所有请求设置头
# 登录(假设返回cookie)
login_response = session.post('https://example.com/login', {
'username': 'alice',
'password': 'secret'
})
# 后续请求自动携带cookie和设置的头
profile_response = session.get('https://example.com/profile')
data_response = session.get('https://example.com/api/data')
# 文件上传
files = {'file': open('document.pdf', 'rb')} # 打开文件
response = requests.post('https://httpbin.org/post', files=files)
files['file'].close() # 记得关闭文件
# 或使用with语句自动关闭文件
with open('document.pdf', 'rb') as f:
files = {'file': f}
response = requests.post('https://httpbin.org/post', files=files)
# 文件下载
response = requests.get('https://example.com/large-file.zip', stream=True)
with open('downloaded-file.zip', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192): # 分块下载
f.write(chunk)
# 错误处理
try:
response = requests.get('https://api.example.com/data', timeout=5)
response.raise_for_status() # 如果状态码>=400则抛出异常
data = response.json()
except requests.exceptions.Timeout:
print("请求超时")
except requests.exceptions.ConnectionError:
print("连接错误")
except requests.exceptions.HTTPError as e:
print(f"HTTP错误: {e}")
except requests.exceptions.RequestException as e:
print(f"请求异常: {e}")
# b、- ORM框架
SQLAlchemy是Python最强大的ORM框架,功能类似Hibernate,但更灵活。
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Boolean, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from datetime import datetime
# 创建基类 - 类似JPA的@Entity基类
Base = declarative_base()
# 定义数据模型 - 类似JPA Entity
class User(Base):
"""用户表模型"""
__tablename__ = 'users' # 指定表名
# 主键 - 类似JPA的@Id @GeneratedValue
id = Column(Integer, primary_key=True, autoincrement=True)
# 字符串字段 - 类似JPA的@Column
name = Column(String(100), nullable=False) # VARCHAR(100) NOT NULL
email = Column(String(255), unique=True, nullable=False) # 唯一约束
# 布尔字段
is_active = Column(Boolean, default=True) # 默认值
# 时间字段
created_at = Column(DateTime, default=datetime.utcnow) # 创建时间
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 关联关系 - 一对多
posts = relationship("Post", back_populates="author") # 用户的所有文章
def __repr__(self):
"""字符串表示 - 类似Java的toString()"""
return f"<User(id={self.id}, name='{self.name}', email='{self.email}')>"
class Post(Base):
"""文章表模型"""
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
content = Column(String(10000)) # 文章内容
# 外键 - 类似JPA的@ManyToOne
author_id = Column(Integer, ForeignKey('users.id'), nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
# 反向关联 - 多对一
author = relationship("User", back_populates="posts")
def __repr__(self):
return f"<Post(id={self.id}, title='{self.title}')>"
# 创建数据库连接 - 类似Spring的DataSource配置
# SQLite示例(文件数据库)
engine = create_engine('sqlite:///blog.db', echo=True) # echo=True显示SQL日志
# MySQL示例
# engine = create_engine('mysql+pymysql://user:password@localhost/blog_db')
# PostgreSQL示例
# engine = create_engine('postgresql://user:password@localhost/blog_db')
# 创建所有表 - 类似Hibernate的ddl-auto=create
Base.metadata.create_all(engine)
# 创建会话工厂 - 类似Spring的EntityManagerFactory
SessionLocal = sessionmaker(bind=engine)
# 数据库操作示例
def create_user_and_posts():
"""创建用户和文章的示例"""
# 创建会话 - 类似JPA的EntityManager
session = SessionLocal()
try:
# 创建用户 - 类似JPA的persist()
user = User(
name='Alice',
email='alice@example.com',
is_active=True
)
session.add(user) # 添加到会话
session.flush() # 立即执行INSERT,获取ID但不提交事务
print(f"创建用户,ID: {user.id}")
# 创建文章
post1 = Post(
title='Python学习笔记',
content='今天学习了SQLAlchemy...',
author_id=user.id
)
post2 = Post(
title='Web开发经验',
content='使用Flask开发API...',
author_id=user.id
)
session.add_all([post1, post2]) # 批量添加
session.commit() # 提交事务 - 类似JPA的commit()
print(f"创建文章: {post1.title}, {post2.title}")
except Exception as e:
session.rollback() # 回滚事务 - 类似JPA的rollback()
print(f"操作失败: {e}")
finally:
session.close() # 关闭会话 - 类似JPA的close()
def query_examples():
"""查询示例"""
session = SessionLocal()
try:
# 查询所有用户 - 类似JPA的findAll()
all_users = session.query(User).all()
print(f"所有用户: {all_users}")
# 根据ID查询 - 类似JPA的findById()
user = session.query(User).get(1) # 主键查询
if user:
print(f"用户1: {user.name}")
# 条件查询 - 类似JPA的findByNameLike()
active_users = session.query(User).filter(User.is_active == True).all()
alice = session.query(User).filter(User.name == 'Alice').first()
# 复杂条件查询
from sqlalchemy import and_, or_
users = session.query(User).filter(
and_(
User.is_active == True,
User.email.like('%@example.com') # LIKE操作
)
).all()
# 排序和分页 - 类似JPA的Pageable
users = session.query(User).order_by(User.created_at.desc()).limit(10).offset(0).all()
# 关联查询 - 类似JPA的JOIN FETCH
users_with_posts = session.query(User).join(Post).all() # INNER JOIN
# 左连接
users_left_join = session.query(User).outerjoin(Post).all() # LEFT JOIN
# 预加载关联数据 - 解决N+1问题
from sqlalchemy.orm import joinedload
users = session.query(User).options(joinedload(User.posts)).all()
for user in users:
print(f"用户: {user.name}")
for post in user.posts: # 不会产生额外查询
print(f" 文章: {post.title}")
# 聚合查询
from sqlalchemy import func
user_count = session.query(func.count(User.id)).scalar() # COUNT查询
avg_posts = session.query(func.avg(func.count(Post.id))).group_by(Post.author_id).scalar()
# 更新操作 - 类似JPA的merge()
user = session.query(User).filter(User.name == 'Alice').first()
if user:
user.email = 'alice.new@example.com' # 直接修改属性
session.commit() # 提交更改
# 批量更新
session.query(User).filter(User.is_active == False).update({
User.is_active: True
})
session.commit()
# 删除操作
post_to_delete = session.query(Post).filter(Post.title.like('%测试%')).first()
if post_to_delete:
session.delete(post_to_delete)
session.commit()
# 批量删除
session.query(Post).filter(Post.created_at < datetime(2020, 1, 1)).delete()
session.commit()
finally:
session.close()
# 使用上下文管理器自动管理会话
from contextlib import contextmanager
@contextmanager
def get_db_session():
"""数据库会话上下文管理器"""
session = SessionLocal()
try:
yield session
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
# 使用示例
def safe_database_operation():
"""安全的数据库操作"""
with get_db_session() as session:
user = User(name='Bob', email='bob@example.com')
session.add(user)
# 自动提交或回滚,自动关闭会话
# 执行示例
if __name__ == '__main__':
create_user_and_posts()
query_examples()
# c、数据库驱动
各种数据库的Python驱动程序,类似JDBC驱动。
# MySQL
import pymysql
conn = pymysql.connect(host='localhost', user='root', password='password', database='test')
# PostgreSQL
import psycopg2
conn = psycopg2.connect(host='localhost', database='test', user='postgres', password='password')
# Redis
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.set('key', 'value')
print(r.get('key'))
# 10.4、测试框架
# a、- 标准库
Python内置的测试框架,类似JUnit,提供基本的测试功能。
import unittest
class TestCalculator(unittest.TestCase):
def setUp(self):
self.calc = Calculator()
def test_add(self):
result = self.calc.add(2, 3)
self.assertEqual(result, 5)
def test_divide_by_zero(self):
with self.assertRaises(ZeroDivisionError):
self.calc.divide(10, 0)
if __name__ == '__main__':
unittest.main()
# b、- 主流测试框架
pytest是Python最流行的第三方测试框架,语法更简洁,功能更强大。
# 安装: pip install pytest
def test_add():
assert add(2, 3) == 5
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
# 参数化测试
import pytest
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(1, 1, 2),
(0, 5, 5)
])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected
# 运行: pytest test_file.py
# c、- 模拟对象
mock库用于创建模拟对象,便于单元测试,类似Mockito。
from unittest.mock import Mock, patch
# Mock对象
mock_api = Mock()
mock_api.get_user.return_value = {'name': 'Alice', 'id': 1}
# 使用patch装饰器
@patch('requests.get')
def test_api_call(mock_get):
mock_get.return_value.json.return_value = {'status': 'ok'}
result = api_call()
assert result['status'] == 'ok'
# 10.5、其他实用库
# a、- HTML解析
BeautifulSoup是HTML/XML解析库,用于网页爬虫和数据提取,类似Jsoup。
from bs4 import BeautifulSoup
import requests
# 网页抓取
response = requests.get('https://example.com')
soup = BeautifulSoup(response.content, 'html.parser')
# 解析HTML
title = soup.find('title').text
links = soup.find_all('a')
for link in links:
print(link.get('href'))
# b、- 图像处理
Pillow是Python图像处理库,支持多种图像格式和基本的图像操作。
from PIL import Image, ImageFilter
# 打开图像
img = Image.open('photo.jpg')
# 基本操作
resized = img.resize((100, 100))
rotated = img.rotate(45)
filtered = img.filter(ImageFilter.BLUR)
# 保存
resized.save('thumbnail.jpg')
# c、python-dotenv - 环境变量管理
python-dotenv用于从.env文件加载环境变量,便于配置管理。
# .env文件
# DATABASE_URL=postgresql://localhost/mydb
# SECRET_KEY=mysecretkey
from dotenv import load_dotenv
import os
# 加载.env文件
load_dotenv()
# 使用环境变量
database_url = os.getenv('DATABASE_URL')
secret_key = os.getenv('SECRET_KEY')
生态对比:
| 领域 | Python库 | Java对应 |
|---|---|---|
| Web框架 | Django, Flask, FastAPI | Spring Boot, Spring MVC |
| ORM | SQLAlchemy | JPA/Hibernate |
| HTTP客户端 | requests, httpx | Apache HttpClient, OkHttp |
| 测试 | pytest, unittest | JUnit, TestNG |
| JSON | json (内置) | Jackson, Gson |
| 日志 | logging (内置) | Logback, Log4j |
| 包管理 | pip, conda | Maven, Gradle |
| 数据科学 | NumPy, Pandas | - (需第三方库) |
# 11、代码质量与工程实践
对于从Java转来的开发者,Python的工程实践可能显得更加灵活,但也需要更强的自律性。Java有严格的编码规范和成熟的工程体系,而Python则提供了更多选择,需要团队建立自己的最佳实践。
# 11.1、代码规范
# a、PEP 8编码规范
PEP 8是Python官方的编码风格指南,相当于Java世界的Google Java Style Guide。
缩进与空格:
# ✅ 正确:使用4个空格缩进
def calculate_total(items):
total = 0
for item in items:
total += item.price
return total
# ❌ 错误:使用Tab缩进
def calculate_total(items):
total = 0
for item in items:
total += item.price
return total
# ✅ 正确:运算符两侧有空格
result = a + b * c
# ✅ 正确:函数参数默认值等号两侧无空格
def create_user(name, age=25, active=True):
pass
# ❌ 错误:参数默认值等号两侧有空格
def create_user(name, age = 25, active = True):
pass
行长度与换行:
# ✅ 正确:每行不超过79字符
def process_user_data(user_id, user_name, email, phone, address, city, country):
return user_id
# ✅ 正确:长行换行使用括号或反斜杠
long_variable_name = some_function_with_very_long_name(
parameter1, parameter2, parameter3, parameter4
)
# 或使用反斜杠
result = (first_value + second_value + third_value +
fourth_value + fifth_value)
# ✅ 正确:二元运算符换行在操作符前
income = (gross_wages
+ taxable_interest
+ (dividends - qualified_dividends)
- ira_deduction
- student_loan_interest)
导入语句:
# ✅ 正确:导入顺序(标准库 -> 第三方库 -> 本地模块)
import os
import sys
from datetime import datetime
import requests
import numpy as np
from myproject.utils import helper
from myproject.models import User
# ✅ 正确:避免使用通配符导入
from math import sqrt, sin, cos # 推荐
# from math import * # 不推荐
# ✅ 正确:多行导入
from collections import (
defaultdict,
OrderedDict,
Counter,
deque
)
对比Java的import风格:
// Java: 按字母顺序,使用通配符是常见的
import java.util.*;
import java.io.*;
import com.example.models.*;
# b、命名约定
Python的命名约定与Java有明显差异:
| 类型 | Python风格 | Java风格 | 示例 |
|---|---|---|---|
| 变量/函数 | snake_case | camelCase | user_name, get_user_info() |
| 类名 | PascalCase | PascalCase | UserService, HttpClient |
| 常量 | UPPER_SNAKE_CASE | UPPER_SNAKE_CASE | MAX_RETRY_COUNT |
| 私有成员 | _prefix | private | _internal_method |
| 模块名 | lowercase | lowercase | user_service.py |
具体示例:
# ✅ 正确的Python命名
class UserService:
MAX_RETRY_COUNT = 3
_instance_count = 0
def __init__(self, db_connection):
self.db_connection = db_connection
UserService._instance_count += 1
def get_user_by_id(self, user_id):
"""根据ID获取用户信息"""
query = f"SELECT * FROM users WHERE id = {user_id}"
return self.db_connection.execute(query)
def _validate_user_data(self, user_data):
"""内部方法:验证用户数据"""
pass
# ✅ 正确的模块级命名
def calculate_total_price(items, tax_rate=0.08):
total = sum(item.price for item in items)
return total * (1 + tax_rate)
API_BASE_URL = "https://api.example.com"
DEFAULT_TIMEOUT = 30
特殊命名约定:
class DatabaseConnection:
def __init__(self):
self._connection = None # 单下划线:受保护成员
self.__password = "secret" # 双下划线:名称改写(真正私有)
def _internal_query(self, sql): # 内部使用的方法
return self._connection.execute(sql)
def __init_connection(self): # 名称改写后变为 _DatabaseConnection__init_connection
pass
def connect(self):
self.__init_connection() # 只能在类内部调用
def __str__(self): # 魔术方法:双下划线前后
return f"DatabaseConnection()"
# c、文档字符串(Docstring)
Python使用docstring代替Java的Javadoc,格式更加简洁。
三种常用docstring格式:
class Calculator:
"""计算器类示例
提供基本的数学运算功能。
Attributes:
precision (int): 计算精度,默认为2位小数
history (list): 计算历史记录
"""
def __init__(self, precision=2):
"""初始化计算器
Args:
precision (int): 计算精度,默认为2位小数
Example:
>>> calc = Calculator(4)
>>> calc.precision
4
"""
self.precision = precision
self.history = []
def add(self, a, b):
"""加法运算
Args:
a (float): 第一个数
b (float): 第二个数
Returns:
float: 两数之和
Raises:
TypeError: 当输入不是数字时
Example:
>>> calc = Calculator()
>>> calc.add(1.5, 2.3)
3.8
"""
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("输入必须是数字")
result = round(a + b, self.precision)
self.history.append(f"{a} + {b} = {result}")
return result
Google风格docstring:
def calculate_bmi(weight, height):
"""计算BMI指数
使用标准BMI公式:BMI = 体重(kg) / 身高(m)²
Args:
weight (float): 体重,单位千克
height (float): 身高,单位米
Returns:
float: BMI指数,保留一位小数
Raises:
ValueError: 当体重或身高为0或负数时
Example:
>>> calculate_bmi(70, 1.75)
22.9
"""
if weight <= 0 or height <= 0:
raise ValueError("体重和身高必须大于0")
bmi = weight / (height ** 2)
return round(bmi, 1)
# d、注释最佳实践
Python的注释风格比Java更简洁,但同样重要。
单行注释:
# ✅ 好的注释:解释为什么,而不是做什么
if user.is_active: # 只处理活跃用户,避免处理已删除用户
send_notification(user)
# ✅ 好的注释:解释复杂算法
# 使用滑动窗口算法,时间复杂度O(n)
max_sum = max_subarray_sum(numbers)
# ❌ 不好的注释:重复代码本身
age = 25 # 设置年龄为25
TODO和FIXME:
class UserService:
def get_user_profile(self, user_id):
# TODO: 添加缓存机制,避免频繁查询数据库
profile = self.db.get_user_profile(user_id)
# FIXME: 这里没有处理用户不存在的情况
return profile
# 11.2、代码检查工具
Python生态提供了丰富的代码质量工具,类似于Java的SonarQube、Checkstyle等。
# a、pylint
pylint是最全面的代码检查工具,类似于Java的SpotBugs。
# 安装pylint
pip install pylint
# 检查单个文件
pylint mymodule.py
# 检查整个包
pylint mypackage/
# 生成报告
pylint --output-format=json:report.json mymodule.py
pylint配置文件 .pylintrc:
[MASTER]
# 指定初始化时加载的模块
load-plugins=pylint_django,pylint_flask
[FORMAT]
# 最大行长度
max-line-length=88
# 缩进风格
indent-string=' '
[DESIGN]
# 最大公共方法数量
max-public-methods=20
# 最大属性数量
max-attributes=7
[MESSAGES CONTROL]
# 禁用的检查
disable=missing-docstring,
too-few-public-methods,
invalid-name
[TYPECHECK]
# 忽略某些类型检查
ignored-classes=optparse.Values,thread._local_dict
常见pylint消息:
# C0111: missing-docstring (缺少文档字符串)
def calculate(x, y):
return x + y
# R0201: no-self-use (方法中没有使用self)
class MyClass:
def utility_method(self): # 应该改为静态方法
return 42
# W0612: unused-variable (未使用的变量)
def process_data():
result, temp = get_result() # temp未使用
return result
# E1101: no-member (对象没有这个属性)
class User:
def __init__(self):
self.name = "张三"
user = User()
print(user.full_name) # E1101: User has no member 'full_name'
# b、flake8
flake8是多个工具的组合,更轻量级,专注于代码风格和常见错误。
# 安装flake8
pip install flake8
# 基本使用
flake8 mymodule.py
# 配置文件
flake8 --config=.flake8 mymodule.py
# 忽略特定错误
flake8 --ignore=E501,W503 mymodule.py
配置文件 .flake8:
[flake8]
max-line-length = 88
extend-ignore = E203, W503
exclude =
.git,
__pycache__,
.venv,
build,
dist
per-file-ignores =
__init__.py:F401
tests/*:S101
常见flake8错误代码:
E系列:错误(Errors)W系列:警告(Warnings)F系列:PyFlakes检查C系列:复杂度检查(mccabe)
# E302: 期望2个空行
def func1(): pass
def func2(): pass
# E501: 行太长
very_long_variable_name = some_function_with_very_long_name(parameter1, parameter2)
# F401: 导入但未使用
import os
import sys # F401: 'sys' imported but unused
# C901: 函数太复杂
def complex_function(data):
if condition1:
if condition2:
if condition3:
# 多层嵌套
pass
# c、black(代码格式化)
black是"不妥协的代码格式化工具",类似于Java的Google Java Format。
# 安装black
pip install black
# 格式化单个文件
black mymodule.py
# 格式化整个项目
black .
# 检查格式但不修改
black --check .
# 显示差异
black --diff mymodule.py
black配置 pyproject.toml:
[tool.black]
line-length = 88
target-version = ['py38', 'py39']
include = '\.pyi?$'
extend-exclude = '''
/(
# 排除的目录
\.eggs
| \.git
| \.venv
| build
| dist
)/
'''
black的格式化示例:
# 格式化前
def complex_function(a,b,c=10,*args,**kwargs):
x=a+b+c
if x>100:
return "large"
else:
return "small"
# black格式化后
def complex_function(a, b, c=10, *args, **kwargs):
x = a + b + c
if x > 100:
return "large"
else:
return "small"
# d、isort(导入排序)
isort专门用于整理import语句,类似于IDE的自动import整理功能。
# 安装isort
pip install isort
# 排序导入
isort mymodule.py
# 检查导入(不修改)
isort --check-only mymodule.py
# 配置文件
isort --settings-file .isort.cfg mymodule.py
配置文件 .isort.cfg:
[settings]
profile = black
multi_line_output = 3
line_length = 88
known_first_party = myproject
known_third_party = requests, numpy, pandas
sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
isort效果示例:
# 排序前
import numpy
from myproject.utils import helper
import os
import sys
from collections import defaultdict
import requests
# isort排序后
import os
import sys
from collections import defaultdict
import numpy
import requests
from myproject.utils import helper
# e、工具集成
pre-commit钩子配置 .pre-commit-config.yaml:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 4.0.1
hooks:
- id: flake8
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint
language: system
types: [python]
安装pre-commit:
pip install pre-commit
pre-commit install
IDE集成配置:
// VSCode settings.json
{
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"python.linting.flake8Enabled": true,
"python.formatting.provider": "black",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
}
# 11.3、虚拟环境管理
Python的虚拟环境相当于Java的Maven/Gradle项目隔离,但更轻量级。
# a、venv实践
创建和使用虚拟环境:
# 创建虚拟环境
python -m venv myproject_env
# 激活虚拟环境
# Windows
myproject_env\Scripts\activate
# macOS/Linux
source myproject_env/bin/activate
# 安装包
pip install requests pandas numpy
# 查看已安装的包
pip list
# 导出依赖
pip freeze > requirements.txt
# 退出虚拟环境
deactivate
项目结构示例:
myproject/
├── .venv/ # 虚拟环境目录
├── src/
│ └── mypackage/
│ ├── __init__.py
│ └── main.py
├── tests/
│ ├── __init__.py
│ └── test_main.py
├── requirements.txt # 生产依赖
├── requirements-dev.txt # 开发依赖
├── .gitignore
├── README.md
└── pyproject.toml # 项目配置
最佳实践:
# 1. 每个项目使用独立的虚拟环境
python -m venv .venv
# 2. 激活环境后安装依赖
source .venv/bin/activate # Linux/Mac
# .venv\Scripts\activate # Windows
# 3. 升级pip
pip install --upgrade pip
# 4. 安装项目依赖
pip install -r requirements.txt
# 5. 开发环境额外依赖
pip install -r requirements-dev.txt
# b、requirements.txt管理
requirements.txt示例:
# 核心依赖
Django==4.2.0
requests==2.31.0
pandas==2.0.3
numpy==1.24.3
# 安全固定
cryptography==41.0.1
urllib3==1.26.16
# 兼容性范围
python-dateutil>=2.8.0,<3.0.0
click>=8.0.0,<9.0.0
requirements-dev.txt示例:
# 包含生产依赖
-r requirements.txt
# 开发工具
pytest==7.4.0
pytest-cov==4.1.0
black==23.3.0
flake8==6.0.0
mypy==1.4.1
# 文档生成
sphinx==7.1.2
sphinx-rtd-theme==1.3.0
版本约束策略:
# 固定版本(生产推荐)
requests==2.31.0
# 兼容版本(允许小版本更新)
requests>=2.30.0,<3.0.0
# 最小版本(不推荐生产使用)
requests>=2.25.0
# 不指定版本(危险)
requests
生成requirements.txt:
# 精确版本
pip freeze > requirements.txt
# 只包含顶级包
pip list --format=freeze > requirements.txt
# 使用pip-tools管理
pip install pip-tools
pip-compile requirements.in # 生成requirements.txt
requirements.in示例:
# requirements.in - 只指定主要依赖
django
requests
pandas>=1.5.0
# c、poetry依赖管理
Poetry是现代Python依赖管理工具,类似于Java的Maven/Gradle。
安装Poetry:
# 官方推荐安装方式
curl -sSL https://install.python-poetry.org | python3 -
# 或使用pip
pip install poetry
初始化项目:
# 创建新项目
poetry new myproject
cd myproject
# 在现有项目中初始化
poetry init
pyproject.toml配置:
[tool.poetry]
name = "myproject"
version = "0.1.0"
description = "My Python project"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
packages = [{include = "myproject"}]
[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.31.0"
pandas = "^2.0.0"
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
black = "^23.3.0"
flake8 = "^6.0.0"
mypy = "^1.4.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.black]
line-length = 88
target-version = ['py38']
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
Poetry常用命令:
# 安装依赖
poetry install
# 只安装生产依赖
poetry install --only=main
# 添加依赖
poetry add requests
poetry add --group dev pytest # 添加开发依赖
# 更新依赖
poetry update
poetry update requests
# 激活虚拟环境并运行命令
poetry run python myscript.py
poetry run pytest
# 进入虚拟环境shell
poetry shell
# 显示依赖树
poetry show --tree
# 导出requirements.txt
poetry export -f requirements.txt --output requirements.txt
# 构建包
poetry build
# 发布到PyPI
poetry publish
Poetry vs pip对比:
| 特性 | pip + requirements.txt | Poetry |
|---|---|---|
| 依赖解析 | 基础 | 高级(解决冲突) |
| 锁定文件 | 手动 | 自动(poetry.lock) |
| 虚拟环境 | 手动管理 | 自动管理 |
| 依赖分组 | 多个文件 | 配置文件中分组 |
| 构建发布 | 需要额外工具 | 内置支持 |
| 脚本管理 | 无 | 内置支持 |
企业级Poetry配置:
# pyproject.toml
[[tool.poetry.source]]
name = "private-pypi"
url = "https://pypi.company.com/simple"
default = false
secondary = true
[tool.poetry.scripts]
myproject-cli = "myproject.cli:main"
[tool.poetry.group.test.dependencies]
pytest = "^7.4.0"
pytest-cov = "^4.1.0"
[tool.poetry.group.docs.dependencies]
sphinx = "^7.1.0"
# 11.4、单元测试
Python的测试生态比Java的JUnit更加灵活和强大。pytest是当前最流行的测试框架,提供了丰富的功能和插件。
# a、pytest基础
安装pytest:
pip install pytest
基本测试示例:
# calculator.py
class Calculator:
def add(self, a, b):
return a + b
def divide(self, a, b):
if b == 0:
raise ValueError("除数不能为0")
return a / b
def is_even(self, number):
return number % 2 == 0
# test_calculator.py
import pytest
from calculator import Calculator
class TestCalculator:
def setup_method(self):
"""每个测试方法前执行"""
self.calc = Calculator()
def test_add_positive_numbers(self):
result = self.calc.add(2, 3)
assert result == 5
def test_add_negative_numbers(self):
result = self.calc.add(-2, -3)
assert result == -5
def test_divide_normal(self):
result = self.calc.divide(10, 2)
assert result == 5
def test_divide_by_zero(self):
with pytest.raises(ValueError, match="除数不能为0"):
self.calc.divide(10, 0)
@pytest.mark.parametrize("number,expected", [
(2, True),
(3, False),
(0, True),
(-4, True),
])
def test_is_even(self, number, expected):
result = self.calc.is_even(number)
assert result == expected
运行测试:
# 运行所有测试
pytest
# 运行特定文件
pytest test_calculator.py
# 运行特定测试方法
pytest test_calculator.py::TestCalculator::test_add_positive_numbers
# 显示详细输出
pytest -v
# 在第一个失败时停止
pytest -x
# 显示覆盖率
pytest --cov=calculator
# b、测试驱动开发(TDD)
TDD在Python中比Java更简洁,让我们通过一个实际例子来展示:
需求:实现一个密码验证器
1. 先写测试(红):
# test_password_validator.py
import pytest
from password_validator import PasswordValidator
class TestPasswordValidator:
def setup_method(self):
self.validator = PasswordValidator()
def test_valid_password(self):
password = "MySecure123!"
assert self.validator.validate(password) == True
def test_too_short_password(self):
password = "Short1!"
assert self.validator.validate(password) == False
def test_missing_uppercase(self):
password = "mysecure123!"
assert self.validator.validate(password) == False
def test_missing_lowercase(self):
password = "MYSECURE123!"
assert self.validator.validate(password) == False
def test_missing_number(self):
password = "MySecure!"
assert self.validator.validate(password) == False
def test_missing_special_char(self):
password = "MySecure123"
assert self.validator.validate(password) == False
def test_get_error_messages(self):
password = "weak"
errors = self.validator.get_errors(password)
assert "密码长度至少8位" in errors
assert "必须包含大写字母" in errors
assert "必须包含小写字母" in errors
assert "必须包含数字" in errors
assert "必须包含特殊字符" in errors
2. 运行测试(失败):
pytest test_password_validator.py
# 会失败,因为PasswordValidator还不存在
3. 实现最小代码(绿):
# password_validator.py
import re
class PasswordValidator:
def __init__(self):
self.min_length = 8
def validate(self, password):
"""验证密码是否符合要求"""
if len(password) < self.min_length:
return False
if not re.search(r'[A-Z]', password):
return False
if not re.search(r'[a-z]', password):
return False
if not re.search(r'\d', password):
return False
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
return False
return True
def get_errors(self, password):
"""获取密码验证的错误信息"""
errors = []
if len(password) < self.min_length:
errors.append("密码长度至少8位")
if not re.search(r'[A-Z]', password):
errors.append("必须包含大写字母")
if not re.search(r'[a-z]', password):
errors.append("必须包含小写字母")
if not re.search(r'\d', password):
errors.append("必须包含数字")
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
errors.append("必须包含特殊字符")
return errors
4. 运行测试(通过):
pytest test_password_validator.py
# 现在应该全部通过
5. 重构(优化):
# 重构后的密码验证器
class PasswordValidator:
def __init__(self, min_length=8):
self.min_length = min_length
self.patterns = {
'uppercase': r'[A-Z]',
'lowercase': r'[a-z]',
'number': r'\d',
'special': r'[!@#$%^&*(),.?":{}|<>]'
}
self.error_messages = {
'length': f"密码长度至少{min_length}位",
'uppercase': "必须包含大写字母",
'lowercase': "必须包含小写字母",
'number': "必须包含数字",
'special': "必须包含特殊字符"
}
def validate(self, password):
"""验证密码是否符合要求"""
return len(self.get_errors(password)) == 0
def get_errors(self, password):
"""获取密码验证的错误信息"""
errors = []
if len(password) < self.min_length:
errors.append(self.error_messages['length'])
for name, pattern in self.patterns.items():
if not re.search(pattern, password):
errors.append(self.error_messages[name])
return errors
6. 再次运行测试(确保重构没有破坏功能):
pytest test_password_validator.py -v
# c、覆盖率测试
安装coverage工具:
pip install pytest-cov
覆盖率报告:
# 生成覆盖率报告
pytest --cov=calculator test_calculator.py
# 生成HTML报告
pytest --cov=calculator --cov-report=html test_calculator.py
# 查看HTML报告
open htmlcov/index.html
# 设置覆盖率阈值
pytest --cov=calculator --cov-fail-under=80 test_calculator.py
覆盖率配置 .coveragerc:
[run]
source = src
omit =
tests/*
venv/*
*/migrations/*
[report]
exclude_lines =
pragma: no cover
def __repr__
raise AssertionError
raise NotImplementedError
if __name__ == .__main__.:
[html]
directory = htmlcov
# d、mock使用
mock用于隔离依赖,类似于Java的Mockito。
基本mock使用:
# user_service.py
import requests
class UserService:
def __init__(self, api_base_url):
self.api_base_url = api_base_url
def get_user(self, user_id):
"""从API获取用户信息"""
response = requests.get(f"{self.api_base_url}/users/{user_id}")
if response.status_code == 200:
return response.json()
return None
def send_welcome_email(self, user_email):
"""发送欢迎邮件"""
# 假设这是调用邮件服务
response = requests.post(
"https://api.emailservice.com/send",
json={"to": user_email, "subject": "Welcome!"}
)
return response.status_code == 200
# test_user_service.py
import pytest
from unittest.mock import Mock, patch
from user_service import UserService
class TestUserService:
def setup_method(self):
self.service = UserService("https://api.example.com")
@patch('user_service.requests.get')
def test_get_user_success(self, mock_get):
"""测试成功获取用户"""
# 模拟API响应
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {
"id": 1,
"name": "张三",
"email": "zhangsan@example.com"
}
mock_get.return_value = mock_response
# 测试
user = self.service.get_user(1)
# 验证
assert user is not None
assert user["name"] == "张三"
mock_get.assert_called_once_with("https://api.example.com/users/1")
@patch('user_service.requests.get')
def test_get_user_not_found(self, mock_get):
"""测试用户不存在"""
mock_response = Mock()
mock_response.status_code = 404
mock_get.return_value = mock_response
user = self.service.get_user(999)
assert user is None
@patch('user_service.requests.post')
def test_send_welcome_email(self, mock_post):
"""测试发送欢迎邮件"""
mock_response = Mock()
mock_response.status_code = 200
mock_post.return_value = mock_response
result = self.service.send_welcome_email("test@example.com")
assert result is True
# 验证调用参数
mock_post.assert_called_once_with(
"https://api.emailservice.com/send",
json={"to": "test@example.com", "subject": "Welcome!"}
)
高级mock技巧:
# test_advanced_mock.py
from unittest.mock import Mock, patch, MagicMock
import pytest
class TestAdvancedMock:
def test_mock_side_effect(self):
"""使用side_effect模拟不同情况"""
mock_func = Mock()
mock_func.side_effect = [200, 404, 500] # 连续调用的不同返回值
assert mock_func() == 200
assert mock_func() == 404
assert mock_func() == 500
def test_mock_exception(self):
"""模拟异常"""
mock_func = Mock()
mock_func.side_effect = ValueError("API错误")
with pytest.raises(ValueError, match="API错误"):
mock_func()
@patch('builtins.open', new_callable=MagicMock)
def test_file_operations(self, mock_open):
"""测试文件操作"""
mock_file = MagicMock()
mock_file.__enter__.return_value = mock_file
mock_file.read.return_value = "file content"
mock_open.return_value = mock_file
# 测试代码
with open("test.txt", "r") as f:
content = f.read()
assert content == "file content"
mock_open.assert_called_once_with("test.txt", "r")
def test_mock_spec(self):
"""使用spec模拟特定接口"""
class DatabaseInterface:
def get_user(self, user_id): pass
def save_user(self, user): pass
mock_db = Mock(spec=DatabaseInterface)
# 正确的方法调用
mock_db.get_user(1)
# 错误的方法调用会引发异常
# mock_db.delete_user(1) # 会报错,因为接口中没有这个方法
mock_db.get_user.assert_called_once_with(1)
pytest fixtures:
# conftest.py
import pytest
from unittest.mock import Mock
@pytest.fixture
def mock_api_client():
"""创建模拟API客户端"""
client = Mock()
client.get_user.return_value = {"id": 1, "name": "测试用户"}
client.create_user.return_value = {"id": 2, "name": "新用户"}
return client
@pytest.fixture
def user_service(mock_api_client):
"""使用模拟API客户端创建用户服务"""
from user_service import UserService
return UserService(mock_api_client)
# test_with_fixtures.py
def test_user_service_with_fixture(user_service, mock_api_client):
"""使用fixture的测试"""
user = user_service.get_user(1)
assert user["name"] == "测试用户"
mock_api_client.get_user.assert_called_once_with(1)
异步测试:
# test_async.py
import pytest
from unittest.mock import AsyncMock, patch
@pytest.mark.asyncio
async def test_async_function():
"""测试异步函数"""
async_func = AsyncMock()
async_func.return_value = "async result"
result = await async_func()
assert result == "async result"
@patch('aiohttp.ClientSession.get')
@pytest.mark.asyncio
async def test_async_api_call(self, mock_get):
"""测试异步API调用"""
mock_response = AsyncMock()
mock_response.status_code = 200
mock_response.json.return_value = {"data": "test"}
mock_get.return_value.__aenter__.return_value = mock_response
async with aiohttp.ClientSession() as session:
response = await session.get("https://api.example.com/data")
data = await response.json()
assert data["data"] == "test"
# 11.5、性能优化
Python的性能优化与Java有很大不同。Python是解释型语言,需要采用不同的优化策略。
# a、timeit性能测试
基本timeit使用:
import timeit
# 测试小代码片段的执行时间
def test_list_comprehension():
return [x**2 for x in range(1000)]
def test_for_loop():
result = []
for x in range(1000):
result.append(x**2)
return result
# timeit测试
list_comp_time = timeit.timeit(test_list_comprehension, number=1000)
for_loop_time = timeit.timeit(test_for_loop, number=1000)
print(f"列表推导式: {list_comp_time:.6f}秒")
print(f"for循环: {for_loop_time:.6f}秒")
print(f"性能提升: {for_loop_time/list_comp_time:.2f}倍")
# 使用timeit.repeat进行多次测试
times = timeit.repeat(test_list_comprehension, number=1000, repeat=5)
print(f"各次执行时间: {[f'{t:.6f}' for t in times]}")
命令行timeit:
python -m timeit "[x**2 for x in range(1000)]"
python -m timeit -n 1000 "sum(range(100))"
python -m timeit -s "import math" "math.sqrt(144)"
性能对比示例:
import timeit
def compare_string_concatenation():
"""比较字符串拼接方法的性能"""
def method1_plus():
result = ""
for i in range(1000):
result += str(i)
return result
def method2_join():
return "".join(str(i) for i in range(1000))
def method3_f_string():
parts = [str(i) for i in range(1000)]
return "".join(parts)
# 测试
time1 = timeit.timeit(method1_plus, number=100)
time2 = timeit.timeit(method2_join, number=100)
time3 = timeit.timeit(method3_f_string, number=100)
print(f"字符串拼接 (+): {time1:.6f}秒")
print(f"字符串拼接 (join): {time2:.6f}秒")
print(f"列表拼接 (f-string): {time3:.6f}秒")
print(f"join比+快 {time1/time2:.2f}倍")
compare_string_concatenation()
# b、cProfile性能分析
使用cProfile:
import cProfile
import pstats
import io
def slow_function():
"""模拟慢函数"""
total = 0
for i in range(1000000):
total += i ** 2
return total
def another_function():
"""另一个函数"""
result = []
for i in range(100000):
result.append(i * 2)
return sum(result)
def main_function():
"""主函数"""
result1 = slow_function()
result2 = another_function()
return result1 + result2
# 创建Profile对象
profiler = cProfile.Profile()
# 开始分析
profiler.enable()
# 执行代码
main_function()
# 停止分析
profiler.disable()
# 输出结果
# 方法1:直接打印到控制台
profiler.print_stats(sort='cumulative')
# 方法2:保存到字符串
s = io.StringIO()
ps = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
ps.print_stats()
print(s.getvalue())
# 方法3:保存到文件
profiler.dump_stats('profile_results.prof')
命令行cProfile:
# 分析Python脚本
python -m cProfile -s cumulative myscript.py
# 按函数名排序
python -m cProfile -s name myscript.py
# 保存分析结果
python -m cProfile -o profile_output.prof myscript.py
# 查看分析结果
python -c "import pstats; pstats.Stats('profile_output.prof').sort_stats('cumulative').print_stats(20)"
分析结果解读:
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.001 0.001 0.150 0.150 myscript.py:25(main_function)
1 0.080 0.080 0.080 0.080 myscript.py:15(slow_function)
1 0.069 0.069 0.069 0.069 myscript.py:20(another_function)
ncalls: 调用次数tottime: 函数自身执行时间(不包括子函数)percall: 平均每次调用时间cumtime: 累计执行时间(包括子函数)
高级性能分析:
import cProfile
import pstats
from functools import wraps
def profile_function(func):
"""性能分析装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
# 创建统计对象
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
print(f"\n=== 性能分析结果: {func.__name__} ===")
stats.print_stats(10) # 显示前10个最耗时的函数
return result
return wrapper
@profile_function
def fibonacci(n):
"""计算斐波那契数列(非优化版本)"""
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# 使用
result = fibonacci(30)
print(f"结果: {result}")
# c、常见性能陷阱
陷阱1:字符串拼接:
# ❌ 低效:在循环中使用+拼接
def inefficient_concatenation(items):
result = ""
for item in items:
result += str(item) + "," # 每次都创建新字符串对象
return result
# ✅ 高效:使用join
def efficient_concatenation(items):
return ",".join(str(item) for item in items)
# 性能对比
import timeit
items = list(range(10000))
time1 = timeit.timeit(lambda: inefficient_concatenation(items), number=10)
time2 = timeit.timeit(lambda: efficient_concatenation(items), number=10)
print(f"低效方法: {time1:.4f}秒")
print(f"高效方法: {time2:.4f}秒")
陷阱2:重复计算:
# ❌ 低效:重复计算
def inefficient_sum(matrix):
total = 0
for row in matrix:
for val in row:
if len(row) > 0: # 每次都计算长度
total += val
return total
# ✅ 高效:缓存计算结果
def efficient_sum(matrix):
total = 0
for row in matrix:
row_length = len(row) # 只计算一次
if row_length > 0:
for val in row:
total += val
return total
陷阱3:不必要的函数调用:
# ❌ 低效:重复调用函数
def inefficient_processing(items):
result = []
for item in items:
if expensive_check(item): # 可能重复计算
result.append(process_item(item))
return result
# ✅ 高效:缓存结果
def efficient_processing(items):
result = []
cache = {}
for item in items:
cache_key = get_cache_key(item)
if cache_key not in cache:
cache[cache_key] = expensive_check(item)
if cache[cache_key]:
result.append(process_item(item))
return result
陷阱4:全局变量访问:
# ❌ 低效:在循环中访问全局变量
CONFIG = {"max_items": 1000, "threshold": 0.5}
def inefficient_filter(items):
result = []
for item in items:
if item.value > CONFIG["threshold"]: # 每次都访问全局变量
result.append(item)
return result
# ✅ 高效:缓存到局部变量
def efficient_filter(items):
result = []
threshold = CONFIG["threshold"] # 缓存到局部变量
for item in items:
if item.value > threshold:
result.append(item)
return result
陷阱5:列表查找操作:
# ❌ 低效:使用in操作查找大列表
large_list = list(range(100000))
def inefficient_search(items_to_find):
results = []
for item in items_to_find:
if item in large_list: # O(n)复杂度
results.append(item)
return results
# ✅ 高效:使用集合
large_set = set(large_list)
def efficient_search(items_to_find):
results = []
for item in items_to_find:
if item in large_set: # O(1)复杂度
results.append(item)
return results
# d、优化建议
1. 使用内置函数和数据结构:
import numpy as np
# ✅ 使用numpy进行数值计算
def efficient_math_operation(data):
arr = np.array(data)
return np.sum(arr ** 2) / len(arr)
# ✅ 使用内置函数
data = [1, 2, 3, 4, 5]
sum_data = sum(data) # 比循环求和快
max_data = max(data) # 比手动查找快
sorted_data = sorted(data) # 比自定义排序快
2. 生成器和迭代器:
# ✅ 使用生成器处理大数据
def process_large_file(filename):
with open(filename, 'r') as f:
for line in f: # 逐行处理,不加载整个文件
yield process_line(line)
# ✅ 使用生成器表达式
large_list = range(1000000)
sum_result = sum(x * x for x in large_list) # 内存高效
3. 多进程和异步编程:
import multiprocessing
from concurrent.futures import ProcessPoolExecutor
# ✅ CPU密集型任务使用多进程
def cpu_intensive_task(data):
return sum(x ** 2 for x in data)
def parallel_processing(data_chunks):
with ProcessPoolExecutor() as executor:
results = list(executor.map(cpu_intensive_task, data_chunks))
return sum(results)
# ✅ I/O密集型任务使用异步
import asyncio
import aiohttp
async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def fetch_multiple_urls(urls):
tasks = [fetch_url(url) for url in urls]
return await asyncio.gather(*tasks)
4. 缓存和记忆化:
from functools import lru_cache
import functools
# ✅ 使用lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
# ✅ 自定义缓存
class SimpleCache:
def __init__(self, maxsize=100):
self.cache = {}
self.maxsize = maxsize
def get(self, key):
return self.cache.get(key)
def set(self, key, value):
if len(self.cache) >= self.maxsize:
self.cache.clear()
self.cache[key] = value
cache = SimpleCache()
def expensive_operation(x):
cached_result = cache.get(x)
if cached_result is not None:
return cached_result
result = complex_calculation(x)
cache.set(x, result)
return result
5. 使用C扩展:
# ✅ 对于性能关键部分,考虑使用Cython或C扩展
# 或者使用已有的高性能库如numpy、pandas、scipy等
import numpy as np
# numpy的实现比纯Python快很多倍
def fast_vector_operation(arr1, arr2):
return np.dot(arr1, arr2) # 使用优化的BLAS库
性能监控工具:
import time
import tracemalloc
from contextlib import contextmanager
@contextmanager
def performance_monitor():
"""性能监控上下文管理器"""
# 开始内存跟踪
tracemalloc.start()
start_time = time.time()
try:
yield
finally:
# 结束计时
end_time = time.time()
current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()
print(f"执行时间: {end_time - start_time:.4f}秒")
print(f"当前内存使用: {current / 1024 / 1024:.2f} MB")
print(f"峰值内存使用: {peak / 1024 / 1024:.2f} MB")
# 使用示例
with performance_monitor():
# 执行需要监控的代码
result = some_expensive_operation()
# 12、总结与展望
# 12.1、Python与Java的各自优势
# a、🔸 Python的核心优势
# 1. 简洁优雅的语法
def calculate_total(items):
return sum(item.price for item in items if item.available)
# 2. 动态类型带来的灵活性
data = {"name": "张三", "age": 30, "skills": ["Python", "Java"]}
data["email"] = "zhangsan@example.com" # 动态添加属性
# 3. 强大的标准库和生态
import pandas as pd
import requests
import numpy as np
import matplotlib.pyplot as plt
# 一行代码完成复杂操作
df = pd.read_csv('data.csv').groupby('category').sum()
📊 Python优势对比表
| 特性 | Python | Java | 适用场景 |
|---|---|---|---|
| 学习曲线 | 平缓,语法简洁 | 较陡,概念复杂 | 快速入门,原型开发 |
| 开发效率 | 极高,代码量少 | 中等,需要较多模板代码 | MVP开发,数据分析 |
| 生态丰富度 | 数据科学、AI领域领先 | 企业级应用生态完善 | 机器学习,Web开发 |
| 性能 | 解释执行,相对较慢 | JIT编译,性能优秀 | 对性能要求极高的场景 |
| 部署 | 简单,无需编译 | 复杂,需要打包部署 | 快速迭代,云原生 |
# b、🔸 Java的核心优势
// 1. 强类型系统,编译时错误检查
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = Objects.requireNonNull(userRepository);
}
// 编译器会检查类型错误
public User findUserById(Long userId) {
return userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(userId));
}
}
// 2. 面向对象的完备支持
public abstract class Animal {
protected String name;
public abstract void makeSound();
public void sleep() {
System.out.println(name + " is sleeping");
}
}
// 3. 成熟的企业级生态
@Service
@Transactional
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public Order createOrder(OrderRequest request) {
// 企业级事务管理
Order order = new Order(request);
return orderRepository.save(order);
}
}
🏢 Java在以下场景中更具优势:
- 大型企业级应用:银行、保险、电信等核心业务系统
- 高性能要求:交易系统、实时数据处理
- 团队协作开发:强类型约束,便于大型团队协作
- 长期维护项目:稳定性好,向后兼容性强
# 12.2、给Java开发者的Python学习寄语
经过前面系统学习,相信你已经对Python有了全面的认识。作为有Java背景的开发者,你在学习Python时具有独特的优势:
** 你的优势:**
- 扎实的编程基础:面向对象思维、设计模式、算法数据结构
- 工程化经验:版本控制、测试驱动、项目管理
- 系统设计能力:架构思维、性能优化、安全考虑
- 问题解决能力:调试技巧、错误排查、逻辑思维
** 学习建议:**
- 保持开放心态:接受Python的动态特性和简洁哲学
- 动手实践:多写代码,多做项目,在实践中掌握精髓
- 对比学习:将Python概念与Java对比,加深理解
- 参与社区:加入Python社区,与其他开发者交流经验
** 未来可期:** 掌握Python将为你打开新的大门:
- 数据科学与AI:进入人工智能领域
- Web开发:快速构建现代Web应用
- 自动化运维:提升工作效率
- 全栈开发:前后端通吃,成为复合型人才
** 最后的话:** 编程语言只是工具,解决问题的思维和能力才是核心竞争力。Java给了你坚实的基础,Python将给你插上灵活的翅膀。保持学习的热情,拥抱技术的变化,你将在编程的道路上走得更远!
祝你变得更强!