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
相关产品推荐
相关产品推荐

