动态更新嵌套数据类值
Update nested dataclass values dynamically
我想知道我正在尝试做的事情是否可行,或者我的方法是否完全错误。
假设我们有 3 个 dataclasses
:
@dataclass
class MyFirstClass:
my_int: int = 1
@dataclass
class MySecondClass:
first_class: MyFirstClass = field(default_factory=MyFirstClass)
my_bool: bool = False
@dataclass
class MainClass:
second_class: MySecondClass = field(default_factory=MySecondClass)
my_str: str = 'Original String'
现在我想更新任何给定的值 class:
def test_this():
# make copy of dataclass
data_object = deepcopy(MainClass())
# first print as is
print(f'\n{asdict(data_object)}')
# update first class
data_object.second_class.first_class.my_int = 2
# update second class
data_object.second_class.my_bool = True
# update main class
data_object.my_str = 'Updated String'
# print updated object
print(f'\n{asdict(data_object)}')
这将输出:
Original:
{'second_class': {'first_class': {'my_int': 1}, 'my_bool': False}, 'my_str': 'Original String'}
Updated:
{'second_class': {'first_class': {'my_int': 2}, 'my_bool': True}, 'my_str': 'Updated String'}
我真的很喜欢做这样的事情的干净方法:
# make copy of dataclass
data_object = deepcopy(MainClass())
data_object.second_class.first_class.my_int = 2
与此相反:
data_object = replace(MainClass(), second_class=MySecondClass(first_class=MyFirstClass(my_int=2)))
基本上,我有一些测试想 运行 通过每个 dataclass
的每个字段的不同数据,所以我想知道是否有某种方法可以动态“获取”正确的要更新的字段?
我想到了最初循环遍历所创建对象的字段,如下所示:
def test_this():
# make copy of dataclass
data_object = deepcopy(MainClass())
# loop through dataclass
for field in fields(data_object):
print(f'\nField: {field}')
print(f'\nField Name: {field.name}')
print(f'Field Type: {field.type}')
print(f'Field Value: {getattr(data_object, field.name)}\n')
输出这个:
Field: Field(name='second_class',type=<class 'tests.sample_test_four.MySecondClass'>,default=<dataclasses._MISSING_TYPE object at 0x106943c10>,default_factory=<class 'tests.sample_test_four.MySecondClass'>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)
Field Name: second_class
Field Type: <class 'tests.sample_test_four.MySecondClass'>
Field Value: MySecondClass(first_class=MyFirstClass(my_int=1), my_bool=False)
Field: Field(name='my_str',type=<class 'str'>,default='Original String',default_factory=<dataclasses._MISSING_TYPE object at 0x106943c10>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)
Field Name: my_str
Field Type: <class 'str'>
Field Value: Original String
但是遍历 MainClass
对象仅将 second_class
和 my_str
显示为可用字段,而不是嵌套 classes 中的所有字段。
然后我想也许我可以将 dataclass
转换为 dict
,然后从那里使用 deep_update
,最后得到类似的东西:
使用pydantic
def test_this():
# create initial dict
data_object = asdict(deepcopy(MainClass()))
# first print as is
print(f'\nOriginal:\n{data_object}')
# update dict
updated_dict = deep_update(data_object, {'my_int': 2})
# print updated
print(f'\nUpdated:\n{updated_dict}')
或者创建我自己的更新函数:
Update value of a nested dictionary of varying depth
def update(d, u):
for k, v in u.items():
if isinstance(v, collections.abc.Mapping):
d[k] = update(d.get(k, {}), v)
else:
d[k] = v
return d
两者都输出:
Original:
{'second_class': {'first_class': {'my_int': 1}, 'my_bool': False}, 'my_str': 'Original String'}
Updated:
{'second_class': {'first_class': {'my_int': 1}, 'my_bool': False}, 'my_str': 'Original String', 'my_int': 2}
问题是它没有更新嵌套字段,而是在最外层添加了一个新字段 dict
此外,使用这种方法,我担心如果 class 中的任何一个具有相同名称的字段,那么哪个会被更新?
所以我不确定我正在尝试做的事情是否可能与获得预期结果所涉及的努力相比,或者我是否应该采用不同的方法?也许使用 JSON
文件而不是 dataclasses
并从那里做一些事情?
我改变了我的方法,我没有使用 dataclasses
来管理这个特定用例的数据注入。
因为无论如何我都会立即将 dataclass
转换为 JSON
对象,所以我想我只需要使用 JSON
文件来存储和注入我需要的数据并更新 dict
相应地。
原始答案归功于:@Gustavo Alves Casqueiro
函数如下:
def update(dictionary: dict[str, any], key: str, value: any, nested_dict_name: str = None) -> dict[str, any]:
if not nested_dict_name: # if current (outermost) dict should be updated
if key in dictionary.keys(): # check if key exists in current dict
dictionary[key] = value
return dictionary
else: # if nested dict should be updated
if nested_dict_name in dictionary.keys(): # check if dict is in next layer
if isinstance(dictionary[nested_dict_name], dict):
if key in dictionary[nested_dict_name].keys(): # check if key exists in current dict
dictionary[nested_dict_name][key] = value
return dictionary
if isinstance(dictionary[nested_dict_name], list):
list_index = random.choice(range(len(dictionary[nested_dict_name]))) # pick a random dict from the list
if key in dictionary[nested_dict_name][list_index].keys(): # check if key exists in current dict
dictionary[nested_dict_name][list_index][key] = value
return dictionary
dic_aux = []
# this would only run IF the above if-statement was not able to identity and update a dict
for val_aux in dictionary.values():
if isinstance(val_aux, dict):
dic_aux.append(val_aux)
# call the update function again for recursion
for i in dic_aux:
return update(dictionary=i, key=key, value=value, nested_dict_name=nested_dict_name)
完整的解决方案可以在这里找到:Update value of a nested dictionary of varying depth
在这里我找到了许多可用的选项来完成我需要做的事情。
老实说,我更愿意使用可以为我完成繁重工作的库,但我就是找不到能满足我需要的东西。
我想知道我正在尝试做的事情是否可行,或者我的方法是否完全错误。
假设我们有 3 个 dataclasses
:
@dataclass
class MyFirstClass:
my_int: int = 1
@dataclass
class MySecondClass:
first_class: MyFirstClass = field(default_factory=MyFirstClass)
my_bool: bool = False
@dataclass
class MainClass:
second_class: MySecondClass = field(default_factory=MySecondClass)
my_str: str = 'Original String'
现在我想更新任何给定的值 class:
def test_this():
# make copy of dataclass
data_object = deepcopy(MainClass())
# first print as is
print(f'\n{asdict(data_object)}')
# update first class
data_object.second_class.first_class.my_int = 2
# update second class
data_object.second_class.my_bool = True
# update main class
data_object.my_str = 'Updated String'
# print updated object
print(f'\n{asdict(data_object)}')
这将输出:
Original:
{'second_class': {'first_class': {'my_int': 1}, 'my_bool': False}, 'my_str': 'Original String'}
Updated:
{'second_class': {'first_class': {'my_int': 2}, 'my_bool': True}, 'my_str': 'Updated String'}
我真的很喜欢做这样的事情的干净方法:
# make copy of dataclass
data_object = deepcopy(MainClass())
data_object.second_class.first_class.my_int = 2
与此相反:
data_object = replace(MainClass(), second_class=MySecondClass(first_class=MyFirstClass(my_int=2)))
基本上,我有一些测试想 运行 通过每个 dataclass
的每个字段的不同数据,所以我想知道是否有某种方法可以动态“获取”正确的要更新的字段?
我想到了最初循环遍历所创建对象的字段,如下所示:
def test_this():
# make copy of dataclass
data_object = deepcopy(MainClass())
# loop through dataclass
for field in fields(data_object):
print(f'\nField: {field}')
print(f'\nField Name: {field.name}')
print(f'Field Type: {field.type}')
print(f'Field Value: {getattr(data_object, field.name)}\n')
输出这个:
Field: Field(name='second_class',type=<class 'tests.sample_test_four.MySecondClass'>,default=<dataclasses._MISSING_TYPE object at 0x106943c10>,default_factory=<class 'tests.sample_test_four.MySecondClass'>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)
Field Name: second_class
Field Type: <class 'tests.sample_test_four.MySecondClass'>
Field Value: MySecondClass(first_class=MyFirstClass(my_int=1), my_bool=False)
Field: Field(name='my_str',type=<class 'str'>,default='Original String',default_factory=<dataclasses._MISSING_TYPE object at 0x106943c10>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)
Field Name: my_str
Field Type: <class 'str'>
Field Value: Original String
但是遍历 MainClass
对象仅将 second_class
和 my_str
显示为可用字段,而不是嵌套 classes 中的所有字段。
然后我想也许我可以将 dataclass
转换为 dict
,然后从那里使用 deep_update
,最后得到类似的东西:
使用pydantic
def test_this():
# create initial dict
data_object = asdict(deepcopy(MainClass()))
# first print as is
print(f'\nOriginal:\n{data_object}')
# update dict
updated_dict = deep_update(data_object, {'my_int': 2})
# print updated
print(f'\nUpdated:\n{updated_dict}')
或者创建我自己的更新函数:
Update value of a nested dictionary of varying depth
def update(d, u):
for k, v in u.items():
if isinstance(v, collections.abc.Mapping):
d[k] = update(d.get(k, {}), v)
else:
d[k] = v
return d
两者都输出:
Original:
{'second_class': {'first_class': {'my_int': 1}, 'my_bool': False}, 'my_str': 'Original String'}
Updated:
{'second_class': {'first_class': {'my_int': 1}, 'my_bool': False}, 'my_str': 'Original String', 'my_int': 2}
问题是它没有更新嵌套字段,而是在最外层添加了一个新字段 dict
此外,使用这种方法,我担心如果 class 中的任何一个具有相同名称的字段,那么哪个会被更新?
所以我不确定我正在尝试做的事情是否可能与获得预期结果所涉及的努力相比,或者我是否应该采用不同的方法?也许使用 JSON
文件而不是 dataclasses
并从那里做一些事情?
我改变了我的方法,我没有使用 dataclasses
来管理这个特定用例的数据注入。
因为无论如何我都会立即将 dataclass
转换为 JSON
对象,所以我想我只需要使用 JSON
文件来存储和注入我需要的数据并更新 dict
相应地。
原始答案归功于:@Gustavo Alves Casqueiro
函数如下:
def update(dictionary: dict[str, any], key: str, value: any, nested_dict_name: str = None) -> dict[str, any]:
if not nested_dict_name: # if current (outermost) dict should be updated
if key in dictionary.keys(): # check if key exists in current dict
dictionary[key] = value
return dictionary
else: # if nested dict should be updated
if nested_dict_name in dictionary.keys(): # check if dict is in next layer
if isinstance(dictionary[nested_dict_name], dict):
if key in dictionary[nested_dict_name].keys(): # check if key exists in current dict
dictionary[nested_dict_name][key] = value
return dictionary
if isinstance(dictionary[nested_dict_name], list):
list_index = random.choice(range(len(dictionary[nested_dict_name]))) # pick a random dict from the list
if key in dictionary[nested_dict_name][list_index].keys(): # check if key exists in current dict
dictionary[nested_dict_name][list_index][key] = value
return dictionary
dic_aux = []
# this would only run IF the above if-statement was not able to identity and update a dict
for val_aux in dictionary.values():
if isinstance(val_aux, dict):
dic_aux.append(val_aux)
# call the update function again for recursion
for i in dic_aux:
return update(dictionary=i, key=key, value=value, nested_dict_name=nested_dict_name)
完整的解决方案可以在这里找到:Update value of a nested dictionary of varying depth
在这里我找到了许多可用的选项来完成我需要做的事情。
老实说,我更愿意使用可以为我完成繁重工作的库,但我就是找不到能满足我需要的东西。