使用attrs包在对象中双重存储传入数据的实现方案
实现数据存储与转换类:Attrs方案与通用最佳实践
嘿,这个需求很典型,我来给你拆解两种靠谱的实现方式——一种是用你提到的attrs包,另一种是不依赖第三方库的通用Python方案,都能完美满足“存原始副本+工作数据”的需求。
一、用attrs包的实现方式
attrs能帮我们简化类的定义,同时保持代码整洁。这里我们可以利用attrs的__attrs_post_init__钩子来处理数据的两份存储,逻辑非常清晰:
首先确保你装了attrs:
pip install attrs
然后看代码:
import attrs @attrs.define class DataProvider: # 初始化时传入的原始数据,用私有字段隐藏 _input_data: attrs.field() # 这两个字段不在初始化时传入,后续赋值 working_data: attrs.field(init=False) original_data: attrs.field(init=False) def __attrs_post_init__(self): # 复制原始数据到只读副本,依赖数据自带的copy()方法 self.original_data = self._input_data.copy() # 把输入数据赋值给工作数据,所有转换操作都用这个 self.working_data = self._input_data # 举个转换方法的例子,你可以替换成自己的逻辑 def normalize_data(self): if not self.working_data: return max_val = max(self.working_data) self.working_data = [x / max_val for x in self.working_data] # 提供getter方法获取原始数据,避免外部直接修改 def get_original_data(self): return self.original_data # 获取处理后的工作数据 def get_processed_data(self): return self.working_data
为什么这么写?
_input_data作为初始化参数,是私有字段,外部不需要直接访问__attrs_post_init__是attrs的生命周期钩子,在实例初始化完成后执行,刚好用来处理数据复制的逻辑,符合attrs的设计风格- 明确区分
original_data(只读原始副本)和working_data(可修改的工作数据),职责清晰
二、通用Python最佳方案(不依赖attrs)
如果不想用第三方库,纯Python类的实现同样简单,核心就是在__init__里处理数据的两份存储:
# 纯Python实现,无需任何第三方库 class DataProvider: def __init__(self, input_data): # 存储原始数据的副本,这里用数据自带的copy() self._original_data = input_data.copy() # 存储用于转换的工作数据 self.working_data = input_data # 示例转换方法,根据你的需求自定义 def normalize_data(self): if not self.working_data: return max_val = max(self.working_data) self.working_data = [x / max_val for x in self.working_data] # 获取原始数据,建议用getter而不是直接暴露字段 def get_original_data(self): # 如果你担心外部修改原始数据,可以返回副本的副本:return self._original_data.copy() return self._original_data def get_processed_data(self): return self.working_data
通用方案的几个关键点:
- 拷贝方式选择:如果你的数据是嵌套结构(比如字典套列表),自带的
copy()可能只是浅拷贝,这时候可以用copy.deepcopy()来确保完全独立的副本:import copy self._original_data = copy.deepcopy(input_data) - 封装性:把
original_data设为私有字段(加下划线_),只通过getter方法暴露,避免外部代码意外修改原始数据 - 扩展性:如果需要多种转换逻辑,可以把转换操作抽象成独立的转换器类,用策略模式来动态切换,比如:
def apply_transformation(self, transformer): self.working_data = transformer.transform(self.working_data) # 自定义转换器示例 class Normalizer: def transform(self, data): max_val = max(data) return [x / max_val for x in data] # 使用方式 provider = DataProvider([1,2,3]) provider.apply_transformation(Normalizer())
最后总结一下核心思路
不管用哪种方案,核心都是这几点:
- 实例化时立即复制原始数据,确保后续的转换操作不会污染原始数据
- 明确区分工作数据(用于所有转换)和原始数据副本(只读,作为基准)的职责
- 根据数据的结构类型,选择合适的拷贝方式(浅拷贝/深拷贝)
内容的提问来源于stack exchange,提问作者n1k31t4
相关产品推荐
相关产品推荐

