3、Python基础速通(下):面向对象编程(类与继承)、异常处理、文件读写、常用标准库(os/sys/json/requests)、虚拟环境管理

好,咱们接着往下聊。上一节我们把Python的基础语法和数据结构捋了一遍,这一节要啃的,是真正让Python变得强大的几个核心模块。说白了,如果你只会写脚本,那叫“会用Python”;但如果你掌握了面向对象、异常处理、文件操作和标准库,你才真正算得上“会Python编程”。

我个人习惯把这一节叫做“从脚本到工程的跨越”。因为接下来要讲的东西,几乎每一个材料信息学项目都会用到。你想想看,处理实验数据要不要读写文件?调用API获取材料数据库要不要用requests?管理不同项目的依赖要不要用虚拟环境?嗯,这些都是基本功。

本章核心知识点一览:

  • 面向对象编程:类与继承,封装你的材料数据
  • 异常处理:让程序在出错时优雅地“摔倒”
  • 文件读写:读写CSV、JSON等常见格式
  • 常用标准库:os、sys、json、requests
  • 虚拟环境管理:隔离项目依赖,避免版本冲突
Python基础速通(下) 面向对象编程 异常处理 文件读写 常用标准库 虚拟环境管理 类与继承 try/except open/with os/sys/json/requests venv / pip

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记录依赖版本。

这些东西,每一个都是实战中高频使用的。别指望一次全记住,先有个印象,写代码时遇到问题再回来查。嗯,这就是编程学习的常态。

📌 课后练习:

  1. 定义一个Perovskite类,继承自Material,添加容忍因子计算方法。
  2. 写一个函数,读取CSV文件中的材料数据,计算平均带隙,处理可能出现的文件不存在或数据格式错误。
  3. 使用requests从某个公开API获取数据,保存为JSON文件。
  4. 为你的项目创建虚拟环境,安装numpy和pandas,导出requirements.txt。

专注资料整理