3、Python基础回顾:变量与数据类型、控制流、函数与装饰器、类与继承
好,咱们直接开工。这一章我把它当成“温润的快速复习”。如果你已经写过一阵Python,可能会觉得某些内容太简单。但别急,我故意挑了一些我当年踩过的坑,还有质控工具里高频出现的写法。你花20分钟过一遍,后面实战的时候会顺很多。
3.1 变量与数据类型
Python是动态类型语言。变量的类型在赋值那一刻才确定,之后再换类型也不报错。这很灵活,但也容易埋雷——我就在生产环境里见过一个变量被多线程乱改类型,导致下游报AttributeError,排查了半天。
type() 或 isinstance() 做类型断言,尤其是处理文件路径、时间戳、数值时。这样团队里其他人接手时一眼就能看到预期类型。
常用的内置类型就几个:
- int / float / complex – 数值计算,注意浮点数精度问题(质控里算误差时要小心)。
- str – 不可变,切片、格式化、正则都离不开。
- list / tuple – 列表可变,元组不可变。我习惯把从配置文件里读出的固定参数用元组存,防止误改。
- dict / set – 字典用于键值映射(质控参数表),集合用于去重或交集运算。
- bool / NoneType – 注意
None不是False,is与==有区别。我曾经在判断空列表时用if list == [],其实用if not list更Pythonic。
来,看个例子:
# 变量动态绑定
count = 10
count = 10.5 # 同一变量,类型从 int 变为 float
# 字典用于质控参数
qc_params = {
"tolerance": 0.01,
"sample_size": 128,
"output_format": "json"
}
# 集合去重
errors = [101, 102, 101, 103, 102]
unique_errors = set(errors)
print(unique_errors) # {101, 102, 103}
3.2 控制流:if / for / while
先说过if。Python里没有switch,但可以用 if-elif-else 链,或者用字典映射。质控工具里经常要根据不同的检测项分发处理函数,我建议用字典+lambda,比一串elif好维护。
def handle_a(data): pass
def handle_b(data): pass
dispatcher = {
"test_a": lambda d: handle_a(d),
"test_b": lambda d: handle_b(d),
"default": lambda d: print("Unknown test")
}
def run_test(test_name, data):
handler = dispatcher.get(test_name, dispatcher["default"])
handler(data)
再说for。我个人觉得 for 是Python里最优雅的循环。能不用 while 就尽量不用,因为我曾用while写了一个死循环——那次是忘了更新循环计数器,CPU跑到100%,服务器差点宕掉。用 for ... in 自带终止,安全。
质控场景里经常需要批量处理文件:
import os
reports = []
for fname in os.listdir("/data/qc_results"):
if fname.endswith(".csv"):
reports.append(parse_csv(fname))
while 更适合不确定次数的循环,比如等待外部设备状态:
import time
while not device_is_ready():
time.sleep(1)
print("Waiting for device...")
| 循环类型 | 使用场景 | 避坑提示 |
|---|---|---|
| for … in | 遍历已知序列 | 不要在循环里修改正在遍历的列表,否则用切片拷贝 |
| while | 条件满足则继续 | 一定要有终止条件,否则死循环 |
3.3 函数定义与装饰器
函数定义很简单:def func(param1, param2=default):。但我特别想聊的是 装饰器。你可能觉得装饰器是“高级特性”,但在质控工具里它几乎天天用——比如给每个测试函数自动加入开始/结束日志、计算运行时长、权限校验。
装饰器的本质:接收一个函数作为参数,返回一个新函数。说白了就是“函数外面再包一层”。
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"[{func.__name__}] 耗时 {elapsed:.3f} 秒")
return result
return wrapper
@timer
def run_quality_check(data):
# 模拟复杂计算
time.sleep(0.1)
return len(data)
# 调用
run_quality_check([1,2,3,4,5])
@functools.wraps,导致被装饰后的函数丢失了原来的 __name__ 和 __doc__。这在单元测试里会出大问题。所以任何时候写装饰器,记得加上 @functools.wraps。
参数化的装饰器更灵活,比如可以传入日志级别:
def log_level(level):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if level == "DEBUG":
print(f"DEBUG: 调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@log_level("DEBUG")
def measure(): pass
3.4 类与继承
关于类,我只需要你记住两个核心点:封装和继承。质控工具里经常需要定义“检测器”基类,然后针对不同检测项派生子类。比如有一个基类 QCChecker,子类实现 check() 方法。
class QCChecker:
def __init__(self, name):
self.name = name
def check(self, data):
raise NotImplementedError("子类必须实现 check 方法")
class RangeChecker(QCChecker):
def __init__(self, name, min_val, max_val):
super().__init__(name)
self.min_val = min_val
self.max_val = max_val
def check(self, data):
return self.min_val <= data <= self.max_val
# 使用
checker = RangeChecker("温度范围", 20.0, 30.0)
result = checker.check(25.5)
print(f"{checker.name}: {result}") # True
继承时要注意 super() 的调用顺序。Python用MRO(方法解析顺序)决定父类查找,多继承的时候容易混乱。我的建议是尽量用组合代替多继承,或者用抽象基类(ABC)严格规定接口。
__repr__ 和 __str__ 让打印对象时显示有意义的信息,调试时省很多眼神。
好了,基础部分先复习到这里。你可能会觉得东西有点碎,但没关系,接下来每个实战章节都会反复用到这些语法——你会在具体场景里把它们彻底练熟。