如何创建Python字典内int、str值的引用实现修改同步?
假设我们有如下字典:
some_dict = { "first": 11111, "second": { "second_1": 22222, "second_2": 232323, "second_3": 99999 } }
当我们把嵌套字典赋值给新变量时,变量持有的是该嵌套字典的引用:
ref = some_dict["second"]
此时修改原字典里嵌套字典的内部值,ref的内容会同步变化:
some_dict["second"]["second_1"] = "not a second_1" print(ref)
执行输出结果为:
{'second_1': 'not a second_1', 'second_2': 232323, 'second_3': 99999}
如何对int、str这类基础类型的值创建同等效果的引用?预期实现效果如下:
ref = some_dict["second"]["second_1"] some_dict["second"]["second_1"] = "not a second_1" print(ref)
预期输出结果:
not a second_1
首先明确Python的变量本质:Python中所有变量存储的都是对象的引用,不存在"值类型直接存储值"的逻辑,但int、str、tuple属于不可变对象,对这类对象的"修改"本质是让变量指向新的对象,而非修改原有对象本身。
之前嵌套字典能实现同步更新,是因为字典是可变对象:修改字典内部键值对时,字典对象本身没有被替换,只是内部内容发生变化,所有指向这个字典对象的引用都会感知到变化。但如果直接把some_dict["second"]["second_1"]赋值给ref,相当于ref直接指向了当时值为22222的int对象,后续给字典键重新赋值,只是让字典的这个键指向了新的字符串对象,和ref之前指向的旧int对象没有关联,自然不会同步。
Python语法层面不支持直接持有"某个容器内某个位置"的透明引用,要实现预期效果,不能直接存储静态值,需要保留取值路径、动态读取最新值,常见实现方案如下:
- 方案1:保存父容器和访问路径,取值时动态读取
这是最轻量的实现方式,不需要额外封装,只需要记录值所在的容器和对应的键,每次取值时从容器中动态获取即可:
如果访问路径较深,可以把完整路径存为元组,配合简单的取值函数实现:# 单层路径的简单实现 ref_parent = some_dict["second"] ref_key = "second_1" some_dict["second"]["second_1"] = "not a second_1" print(ref_parent[ref_key]) # 输出:not a second_1def get_ref(root, path): cur = root for k in path: cur = cur[k] return cur ref_root = some_dict ref_path = ("second", "second_1") some_dict["second"]["second_1"] = "not a second_1" print(get_ref(ref_root, ref_path)) # 输出:not a second_1 - 方案2:用包装类封装取值逻辑
如果希望ref可以像普通值一样直接打印、参与运算,可以自定义简单的包装类,内部保存父容器和键,重写输出、运算相关的魔法方法:class Ref: def __init__(self, container, key): self.container = container self.key = key def __repr__(self): return repr(self.container[self.key]) # 可根据需要重写__add__、__eq__等运算方法适配int/str操作 ref = Ref(some_dict["second"], "second_1") some_dict["second"]["second_1"] = "not a second_1" print(ref) # 输出:not a second_1
注意:不存在完全透明的基础类型引用。哪怕是之前的字典引用场景,如果你直接给
some_dict["second"]赋值一个全新的字典对象,原来的ref也不会同步变化,本质逻辑和基础类型完全一致:只要你替换了容器内存储的对象本身,而不是修改对象内部内容,旧的引用就不会感知到变化。
内容的提问来源于stack exchange,提问作者user18196171

