You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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())
    

最后总结一下核心思路

不管用哪种方案,核心都是这几点:

  1. 实例化时立即复制原始数据,确保后续的转换操作不会污染原始数据
  2. 明确区分工作数据(用于所有转换)和原始数据副本(只读,作为基准)的职责
  3. 根据数据的结构类型,选择合适的拷贝方式(浅拷贝/深拷贝)

内容的提问来源于stack exchange,提问作者n1k31t4

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:04:52