3、Python基础速通(下):面向对象编程(类与继承)、异常处理、文件读写、常用标准库(os/sys/json/requests)、虚拟环境管理
好,咱们接着往下聊。上一节我们把Python的基础语法和数据结构捋了一遍,这一节要啃的,是真正让Python变得强大的几个核心模块。说白了,如果你只会写脚本,那叫“会用Python”;但如果你掌握了面向对象、异常处理、文件操作和标准库,你才真正算得上“会Python编程”。
我个人习惯把这一节叫做“从脚本到工程的跨越”。因为接下来要讲的东西,几乎每一个材料信息学项目都会用到。你想想看,处理实验数据要不要读写文件?调用API获取材料数据库要不要用requests?管理不同项目的依赖要不要用虚拟环境?嗯,这些都是基本功。
本章核心知识点一览:
- 面向对象编程:类与继承,封装你的材料数据
- 异常处理:让程序在出错时优雅地“摔倒”
- 文件读写:读写CSV、JSON等常见格式
- 常用标准库:os、sys、json、requests
- 虚拟环境管理:隔离项目依赖,避免版本冲突
3.1 面向对象编程:把材料数据“封装”起来
面向对象编程,简称OOP。说白了,就是把数据和操作数据的方法打包在一起。为什么要这么做?因为真实世界里的东西,比如一种材料,它既有属性(密度、带隙、晶格常数),也有行为(计算能带、预测稳定性)。用类来建模,最自然不过了。
我在做材料数据库项目时,一开始用字典存数据,后来发现代码越写越乱。改用类之后,整个世界都清净了。
3.1.1 定义一个材料类
class Material:
"""材料基类"""
def __init__(self, name, band_gap, density):
self.name = name
self.band_gap = band_gap # 带隙,单位eV
self.density = density # 密度,单位g/cm³
def describe(self):
return f"{self.name}: 带隙={self.band_gap}eV, 密度={self.density}g/cm³"
def is_semiconductor(self):
"""判断是否为半导体(带隙在0~4eV之间)"""
return 0 < self.band_gap < 4
你看,这个类把材料的属性和行为都封装在一起了。用起来也很直观:
si = Material("硅", 1.12, 2.33)
print(si.describe()) # 硅: 带隙=1.12eV, 密度=2.33g/cm³
print(si.is_semiconductor()) # True
💡 我的习惯:类名用大驼峰命名法(Material),方法名用小写加下划线(describe)。这个约定在Python社区是通用的,别搞反了。
3.1.2 继承:让子类拥有父类的“基因”
继承是OOP的精髓。你想想看,钙钛矿材料也是材料,它应该继承Material的所有属性和方法,再加上自己特有的东西。
class Perovskite(Material):
"""钙钛矿材料,继承自Material"""
def __init__(self, name, band_gap, density, a, b, c):
# 调用父类的构造方法
super().__init__(name, band_gap, density)
self.a = a # 晶格常数a
self.b = b # 晶格常数b
self.c = c # 晶格常数c
def volume(self):
"""计算晶胞体积"""
return self.a * self.b * self.c
# 重写父类方法
def describe(self):
base = super().describe()
return f"{base}, 晶胞体积={self.volume():.2f}ų"
用起来:
mapbi3 = Perovskite("MAPbI₃", 1.55, 4.15, 6.28, 6.28, 6.31)
print(mapbi3.describe()) # MAPbI₃: 带隙=1.55eV, 密度=4.15g/cm³, 晶胞体积=248.97ų
print(mapbi3.is_semiconductor()) # True,继承自父类
⚠️ 避坑指南:我曾经在项目里忘记调用super().__init__(),结果子类实例化时父类的属性全没初始化,调试了半天。记住:子类重写__init__时,一定要手动调用父类的构造方法。
3.2 异常处理:让程序优雅地“摔倒”
写代码哪有不报错的?关键是报错之后怎么办。异常处理就是给程序穿上“护具”,让它摔倒时别摔得太难看。
3.2.1 try/except 的基本用法
try:
# 可能出错的代码
band_gap = float(input("请输入带隙值(eV): "))
if band_gap < 0:
raise ValueError("带隙不能为负数!")
print(f"带隙为 {band_gap}eV")
except ValueError as e:
print(f"输入错误: {e}")
except Exception as e:
print(f"未知错误: {e}")
else:
print("输入有效,程序继续执行")
finally:
print("无论是否出错,这行都会执行")
为什么会这样?因为try块里的代码一旦抛出异常,程序会立即跳转到对应的except块。else块在没异常时执行,finally块无论如何都会执行——这个特性特别适合用来关闭文件或释放资源。
💡 我的经验:不要用裸的except:,它会捕获所有异常,包括KeyboardInterrupt(Ctrl+C)。至少写成except Exception as e,这样你能知道具体是什么错误。
3.3 文件读写:和硬盘打交道
材料信息学离不开文件操作。实验数据存在CSV里,计算结果存在JSON里,配置文件可能是YAML。Python的文件操作,你得玩得转。
3.3.1 读写文本文件
# 写入文件
with open("materials.txt", "w", encoding="utf-8") as f:
f.write("硅,1.12,2.33\n")
f.write("砷化镓,1.43,5.32\n")
# 读取文件
with open("materials.txt", "r", encoding="utf-8") as f:
for line in f:
name, bg, den = line.strip().split(",")
print(f"{name}: 带隙={bg}eV, 密度={den}g/cm³")
注意那个with语句。它叫上下文管理器,能自动关闭文件。我见过太多人忘记f.close()导致文件损坏了,用with就永远不会忘。
3.3.2 读写JSON文件
JSON是材料数据交换的“通用语言”。
import json
# 材料数据
materials = [
{"name": "硅", "band_gap": 1.12, "density": 2.33},
{"name": "砷化镓", "band_gap": 1.43, "density": 5.32}
]
# 写入JSON
with open("materials.json", "w", encoding="utf-8") as f:
json.dump(materials, f, indent=2, ensure_ascii=False)
# 读取JSON
with open("materials.json", "r", encoding="utf-8") as f:
data = json.load(f)
for m in data:
print(m["name"])
⚠️ 注意:ensure_ascii=False这个参数一定要加,否则中文会被转义成\uXXXX。我曾经因为这个被坑过,JSON文件里全是乱码。
3.4 常用标准库:Python的“瑞士军刀”
Python之所以强大,很大程度上是因为它的标准库。下面这四个,是材料信息学项目里最常用的。
3.4.1 os模块:和操作系统打交道
import os
# 获取当前工作目录
cwd = os.getcwd()
print(f"当前目录: {cwd}")
# 创建目录
os.makedirs("data/experiments", exist_ok=True)
# 列出目录下所有文件
for f in os.listdir("data"):
if f.endswith(".csv"):
print(f"找到CSV文件: {f}")
# 拼接路径(跨平台安全)
file_path = os.path.join("data", "experiments", "result.csv")
os.path.join这个函数我几乎每天都在用。为什么?因为Windows用反斜杠,Linux用正斜杠,手动拼接必死无疑。
3.4.2 sys模块:和Python解释器打交道
import sys
# 获取命令行参数
print(f"脚本名: {sys.argv[0]}")
print(f"参数列表: {sys.argv[1:]}")
# 获取Python版本
print(f"Python版本: {sys.version}")
# 添加模块搜索路径
sys.path.append("/home/user/my_modules")
# 优雅退出
sys.exit(0) # 0表示正常退出
3.4.3 json模块:数据交换的“通用语言”
前面已经讲过了,这里补充一个实用技巧:
import json
# 自定义类的JSON序列化
class Material:
def __init__(self, name, band_gap):
self.name = name
self.band_gap = band_gap
def to_dict(self):
return {"name": self.name, "band_gap": self.band_gap}
si = Material("硅", 1.12)
json_str = json.dumps(si.to_dict(), indent=2)
print(json_str)
3.4.4 requests模块:从网络获取数据
这个不是标准库,需要安装:pip install requests。但它是材料信息学的“刚需”——你要从Materials Project、COD等数据库获取数据,全靠它。
import requests
# 从API获取材料数据(示例)
url = "https://api.materialsproject.org/materials/summary/"
headers = {"X-API-KEY": "你的API密钥"}
params = {"formula": "Si", "fields": "band_gap,density"}
try:
response = requests.get(url, headers=headers, params=params, timeout=10)
response.raise_for_status() # 检查HTTP状态码
data = response.json()
print(f"获取到 {len(data)} 条材料数据")
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
💡 我的建议:永远给requests加timeout参数。我曾经写过一个爬虫,没设timeout,结果某个API挂了,程序卡在那里整整一个周末。
3.5 虚拟环境管理:隔离项目依赖
这是新手最容易忽略,但老手最重视的一环。你想想看,项目A需要numpy 1.19,项目B需要numpy 1.24,装在一起不就冲突了吗?虚拟环境就是解决这个问题的。
3.5.1 使用venv创建虚拟环境
# 创建虚拟环境(Python 3.3+自带)
python -m venv myenv
# 激活虚拟环境
# Windows:
myenv\Scripts\activate
# macOS/Linux:
source myenv/bin/activate
# 安装依赖
pip install numpy pandas requests
# 导出依赖列表
pip freeze > requirements.txt
# 退出虚拟环境
deactivate
3.5.2 使用requirements.txt管理依赖
# 在另一台机器上还原环境
python -m venv newenv
source newenv/bin/activate
pip install -r requirements.txt
⚠️ 避坑指南:我曾经犯过一个错误——把虚拟环境目录提交到了Git仓库。虚拟环境里可能有几百MB的文件,而且不同平台不兼容。正确的做法是:只提交requirements.txt,然后在.gitignore里加上myenv/。
3.6 本章小结
好,这一节的内容确实不少。我们来捋一下:
- 面向对象编程:用类封装数据和行为,用继承复用代码。材料信息学里,每个材料都可以是一个对象。
- 异常处理:用try/except让程序在出错时优雅处理,而不是直接崩溃。
- 文件读写:用with open读写文本文件,用json.dump/load处理JSON数据。
- 常用标准库:os管文件路径,sys管解释器,json管数据交换,requests管网络请求。
- 虚拟环境:用venv隔离项目依赖,用requirements.txt记录依赖版本。
这些东西,每一个都是实战中高频使用的。别指望一次全记住,先有个印象,写代码时遇到问题再回来查。嗯,这就是编程学习的常态。
📌 课后练习:
- 定义一个Perovskite类,继承自Material,添加容忍因子计算方法。
- 写一个函数,读取CSV文件中的材料数据,计算平均带隙,处理可能出现的文件不存在或数据格式错误。
- 使用requests从某个公开API获取数据,保存为JSON文件。
- 为你的项目创建虚拟环境,安装numpy和pandas,导出requirements.txt。