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

Python类装饰器如何调用被装饰类的实例属性?

问题描述

我想给dataclass类OG的get_local_context方法添加装饰器,实现将方法返回的数据保存至文件。为避免所有实例覆盖同一文件,我通过field(init=False)和__post_init__方法,根据device属性为每个实例动态生成独立的filename。但当前代码中,装饰器@backup_to_json传入的filename是Field对象而非实例计算后的字符串,报错信息为:Failed to save backup to Field(name='filename',type=<class 'str'>,...),无法生成对应实例的备份文件。

原代码如下:

def write_json(data, filename):
    try:
        with open(filename, 'w') as outfile:
            json.dump(data, outfile)
            return True
    except:
        return False
    
def backup_to_json(filename, write_function):
    def inner(func):
        def wrapper(*args, **kwargs):
            data = func(*args, **kwargs)
            success = write_function(data, filename)
            if success:
                print(f'Backup saved to {filename}')
            else:
                print(f'Failed to save backup to {filename}')
                exit(1)
            return data
        return wrapper
    return inner

@dataclass
class OG:
    device: Devices  # this is an object
    write_function = write_json
    filename: str = field(init=False)

    def __post_init__(self):
        self.filename = str(self.device["name"]) + "_backup.json"
   
    @backup_to_json(filename=filename, write_function=write_function)
    def get_local_context(self):
        return self.device.local_context_data
        

dev = {"name": "mydev"}
dev_status, dev_result = session.get_device(**dev)
if dev_status:    
    dev_obj = OG(device=dev_result)
    local_context = dev_obj.get_local_context()        
解决方案

问题核心是装饰器在类定义阶段就会执行,此时传入的filename是类级别的Field对象,而非实例初始化后生成的字符串。要解决这个问题,需修改装饰器逻辑,让它从方法的实例参数(self)中动态获取filename和write_function。

修改后的完整代码:

import json
from dataclasses import dataclass, field

def write_json(data, filename):
    try:
        with open(filename, 'w') as outfile:
            json.dump(data, outfile)
            return True
    except Exception as e:
        print(f"保存失败原因: {str(e)}")
        return False

def backup_to_json():
    # 不再接收类级参数,改为从实例中获取动态值
    def inner(func):
        def wrapper(self, *args, **kwargs):
            # 通过self(当前实例)获取已生成的filename和write_function
            data = func(self, *args, **kwargs)
            success = self.write_function(data, self.filename)
            if success:
                print(f'Backup saved to {self.filename}')
            else:
                print(f'Failed to save backup to {self.filename}')
                exit(1)
            return data
        return wrapper
    return inner

@dataclass
class OG:
    device: dict  # 适配Devices对象的索引特性
    write_function = write_json
    filename: str = field(init=False)

    def __post_init__(self):
        self.filename = str(self.device["name"]) + "_backup.json"
   
    @backup_to_json()  # 无需传参,从实例动态获取
    def get_local_context(self):
        return self.device.local_context_data

# 测试逻辑
dev = {"name": "mydev"}
dev_status, dev_result = session.get_device(**dev)
if dev_status:    
    dev_obj = OG(device=dev_result)
    local_context = dev_obj.get_local_context()        

关键修改说明

  • 装饰器重构:移除backup_to_json的入参,改为在wrapper中通过self(方法的第一个参数,即当前实例)获取已经生成的filename和write_function,确保每个实例使用独立的文件名。
  • 异常处理优化:为write_json的except添加具体异常捕获,便于排查保存失败的原因。
  • 装饰器调用调整:@backup_to_json()不再传入类级别的Field对象,改为空括号调用,让装饰器从实例动态获取所需值。

修改后,每个设备实例调用get_local_context时,会自动使用自身生成的文件名,比如设备mydev1对应mydev1_backup.json,不会出现文件覆盖问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 05:47:42