3. Python基础速通(下):函数与模块、文件读写、面向对象与异常处理
好,咱们接着聊。上一章我们把Python的变量、流程控制这些基本功过了一遍。这一章,才是真正让你能写出“能用”的代码的关键。说白了,前面那些是砖块,这一章教你怎么用砖块盖房子。
我个人习惯把Python编程分为两个阶段:能写脚本和能写工程。这一章的内容,就是帮你从“能写脚本”跨入“能写工程”的门槛。咱们一个一个来。
3.1 函数与模块:别再写重复代码了
我在材料数据处理项目里见过最头疼的事,就是有人把同一段计算代码复制粘贴了二十遍。改一个参数,得改二十个地方。嗯,这就是典型的“还没学会函数”。
函数,说白了就是把一段逻辑打包,起个名字,随用随调。Python里用def关键字定义函数。
def calculate_density(mass, volume):
"""计算材料密度(单位:g/cm³)"""
if volume <= 0:
return None # 体积不能为零或负数
return mass / volume
# 调用
density = calculate_density(12.5, 4.2)
print(f"密度为:{density:.2f} g/cm³")
参数传递这块有个坑。Python的参数传递是“传对象引用”。对于可变对象(比如列表),函数内部修改会影响到外部。
def add_element(data, element):
data.append(element) # 这会修改原列表
my_list = [1, 2, 3]
add_element(my_list, 4)
print(my_list) # 输出 [1, 2, 3, 4] —— 原列表被改了!
data = data.copy()。
模块就是多个函数的集合文件。一个.py文件就是一个模块。用import导入。
# 假设我们有一个文件 materials_utils.py
# 里面定义了 calculate_density, calculate_stress 等函数
# 在另一个文件中使用
import materials_utils
d = materials_utils.calculate_density(10, 2.5)
# 或者只导入特定函数
from materials_utils import calculate_density
你想想看,把常用的材料计算函数整理成一个模块,每次新项目直接import,是不是省事多了?
3.2 文件读写:CSV和JSON是材料科学的好朋友
做材料科学的,谁没跟实验数据打过交道?CSV和JSON就是最常见的两种数据格式。CSV适合表格数据,JSON适合结构化数据。
读写CSV,我推荐用Python内置的csv模块,简单直接。
import csv
# 写入CSV
data = [
["样品编号", "成分", "密度(g/cm³)", "硬度(HRC)"],
["S001", "Fe-0.2C", 7.85, 22],
["S002", "Fe-0.4C", 7.82, 35],
["S003", "Fe-0.6C", 7.80, 48]
]
with open("steel_samples.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(data)
# 读取CSV
with open("steel_samples.csv", "r", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)
with open(...) as f这种写法。它会自动关闭文件,哪怕中间出错了也会关。我见过有人手动写f.close(),结果异常时没执行到,文件句柄泄露了。
JSON在材料科学里常用于存储复杂的配置信息或嵌套数据。比如一个材料的多层结构描述。
import json
# 一个复合材料的结构描述
composite = {
"name": "碳纤维增强环氧树脂",
"layers": [
{"material": "碳纤维", "thickness_mm": 0.2, "orientation": 0},
{"material": "环氧树脂", "thickness_mm": 0.1, "orientation": None},
{"material": "碳纤维", "thickness_mm": 0.2, "orientation": 90}
],
"properties": {
"density": 1.6,
"tensile_strength": 1200
}
}
# 写入JSON
with open("composite.json", "w", encoding="utf-8") as f:
json.dump(composite, f, indent=2, ensure_ascii=False)
# 读取JSON
with open("composite.json", "r", encoding="utf-8") as f:
data = json.load(f)
print(data["name"])
我个人习惯用indent=2让JSON文件可读性好一点。调试的时候直接打开看,一目了然。
3.3 面向对象编程基础:把材料当成“对象”来思考
面向对象编程(OOP)听起来高大上,其实核心思想很简单:把数据和操作数据的方法打包在一起。在材料科学里,这太自然了——一个材料样品,既有属性(成分、密度、硬度),又有行为(计算强度、模拟热处理)。
class SteelSample:
"""钢材样品类"""
def __init__(self, sample_id, composition, density):
self.sample_id = sample_id
self.composition = composition
self.density = density
self.hardness = None # 初始未知
def set_hardness(self, value):
"""设置硬度值"""
if value < 0:
raise ValueError("硬度不能为负数")
self.hardness = value
def calculate_stress(self, force, area):
"""计算应力 σ = F/A"""
if area <= 0:
return None
return force / area
def info(self):
"""返回样品信息字符串"""
h = f"{self.hardness} HRC" if self.hardness else "未测量"
return f"样品{self.sample_id}:{self.composition},密度{self.density}g/cm³,硬度{h}"
# 使用
s1 = SteelSample("S001", "Fe-0.2C", 7.85)
s1.set_hardness(22)
print(s1.info())
继承是OOP的另一个核心。比如我们有SteelSample,还可以有CeramicSample,它们都继承自一个基类MaterialSample。
class MaterialSample:
def __init__(self, sample_id):
self.sample_id = sample_id
def info(self):
return f"样品编号:{self.sample_id}"
class SteelSample(MaterialSample):
def __init__(self, sample_id, composition):
super().__init__(sample_id) # 调用父类构造
self.composition = composition
def info(self):
# 重写父类方法
return f"{super().info()},成分:{self.composition}"
3.4 异常处理:让程序学会“扛揍”
程序跑着跑着就崩了,这种事谁没遇到过?异常处理就是给程序穿上防弹衣。不是不让它出错,而是出错时知道怎么处理。
def read_experiment_data(filename):
"""安全地读取实验数据文件"""
try:
with open(filename, "r", encoding="utf-8") as f:
data = f.read()
# 尝试解析数据
values = [float(x) for x in data.split(",")]
return values
except FileNotFoundError:
print(f"错误:文件 {filename} 不存在")
return []
except ValueError:
print(f"错误:文件 {filename} 中包含非数字数据")
return []
except Exception as e:
print(f"未知错误:{e}")
return []
finally:
print("数据读取操作完成") # 不管有没有异常,都会执行
为什么会这样?因为实验数据文件经常会有各种意外——格式不对、编码问题、数据缺失。如果不做异常处理,一个文件读不了,整个程序就停了。
except Exception一把抓。结果程序不崩了,但数据错了也不知道。后来我改成except FileNotFoundError、except ValueError这样分开处理,该报错的报错,该重试的重试,程序才真正“健壮”起来。
自定义异常也很有用。比如在材料数据处理中,可以定义InvalidSampleError、DataRangeError等,让错误信息更有业务含义。
class InvalidSampleError(Exception):
"""样品数据无效时抛出的异常"""
pass
def validate_sample(density, hardness):
if density <= 0:
raise InvalidSampleError(f"密度值 {density} 无效")
if hardness < 0 or hardness > 100:
raise InvalidSampleError(f"硬度值 {hardness} 超出合理范围")
return True
好了,这一章的内容就这些。函数和模块让你告别重复代码,文件读写让你能和实验数据打交道,面向对象让代码结构更清晰,异常处理让程序更稳定。这四个技能组合起来,你已经能写出像模像样的材料数据处理工具了。
记住,写代码和做实验一样——多练,多踩坑,多总结。下次遇到问题,先想想能不能用函数封装,数据要不要存成CSV,能不能抽象成一个类,会不会有异常需要处理。慢慢来,这些都会变成你的肌肉记忆。
公众号:蓝海资料掘金营,微信deep3321