删除列表列表中的共享引用?
Remove shared references in list-of-list?
好吧,让我用一个简单的例子来说明问题:
l = [[0]]*3 # makes the array [[0], [0], [0]]
l[0][0] = 42 # l becomes [[42], [42], [42]]
from copy import deepcopy
m = deepcopy(l) # m becomes [[42], [42], [42]]
m[0][0] = 2 # m becomes [[2], [2], [2]]
这是一个基本的共享引用问题。除了通常,当出现这样的问题时,deepcopy
是我们的朋友。
目前,我这样做是为了解决我的 deepcopy
背叛问题:
l = [[0]]*3 # makes the array [[0], [0], [0]]
import JSON
m = JSON.loads(JSON.dumps(l)) # m becomes [[0], [0], [0]] with no self references
我正在寻找一种处理自共享引用的低效且不那么愚蠢的方法。
当然我不会故意制作这样的数组,但我需要处理有人将数组提供给我的代码的情况。 运行 我的 "solution" 在大型数组上速度很慢,而且我有很多级别的嵌套数组,我不能为那些野兽制作这么大的字符串。
l = [[0] for _ in range(3)]
l[0][0] = 2
print(l)
打印:
[[2], [0], [0]]
另外,在您的代码中 deepcopy()
给出了它所做的输出,因为您传入了一个列表,其中已经包含共享相同引用的元素。
from copy import deepcopy
m = deepcopy(l)
m[0][0] = 3
print(l) # prints [[2], [0], [0]]
print(m) # prints [[3], [0], [0]]
你可以在这里看到它完全符合我们的预期
不确定它是否有效,但您可以尝试:
l = [deepcopy(elt) for elt in l]
这是一种适用于列表、字典和不可变值的任意组合的方法。
def very_deep_copy(obj):
if isinstance(obj, list):
return [very_deep_copy(item) for item in obj]
elif isinstance(obj, dict):
return {k: very_deep_copy(v) for k,v in obj.items()}
else:
return obj
l = [[0]]*3
m = very_deep_copy(l)
m[0][0] = 2
print(m)
结果:
[[2], [0], [0]]
假设只有结构类型是列表,这应该适用于任意深度和复杂度的列表:
def customcopy(l):
return [customcopy(e) if type(e) is list else e for e in l]
l = [[0]]*3
x = customcopy(l)
x[0][0] = 3
>>> x
[[3], [0], [0]]
我要挑战复制共享对象才是正确做法的假设。你这么说
Of course I wouldn't make arrays like that on purpose, but I need to handle the case where someone gives one to my code.
但是,如果有人向您传递了意外共享对象的输入,则他们的代码存在错误。如果您的代码注意到错误,您的代码应该通过抛出异常告诉他们,以帮助他们修复错误。
大多数代码只是假设输入没有任何不需要的对象共享。如果你想检测它,手动遍历可能是你最好的选择,特别是因为你的输入预计是 JSON-serializable:
def detect_duplicate_references(data):
_detect_duplicate_references(data, set())
def _detect_duplicate_references(data, memo):
if isinstance(data, (dict, list)):
if id(data) in memo:
raise ValueError(f'multiple references to object {data} detected in input')
memo.add(id(data))
if isinstance(data, list):
for obj in data:
_detect_duplicate_references(obj, memo)
if isinstance(data, dict):
for obj in data.values():
_detect_duplicate_references(obj, memo)
对于一层深拷贝,你可以直接查看参考资料。对于更深的副本,只需递归执行此操作即可。
from copy import deepcopy
def copy_value(l):
l = list(l)
new_list = []
while l:
item = l.pop(0)
if item not in new_list:
new_list.append(item)
else:
new_list.append(deepcopy(item))
return new_list
l = [[0]]*3
m = copy_value(l)
m[0][0] = 2
print(m)
打印
[[2], [0], [0]]
列表理解的另一种方式:
def super_deep_copy(l):
return [i.copy() for i in l]
l = [[0]]*3
l = super_deep_copy(l)
l[0][0] = 2
现在:
print(l)
是:
[[2], [0], [0]]
好吧,让我用一个简单的例子来说明问题:
l = [[0]]*3 # makes the array [[0], [0], [0]]
l[0][0] = 42 # l becomes [[42], [42], [42]]
from copy import deepcopy
m = deepcopy(l) # m becomes [[42], [42], [42]]
m[0][0] = 2 # m becomes [[2], [2], [2]]
这是一个基本的共享引用问题。除了通常,当出现这样的问题时,deepcopy
是我们的朋友。
目前,我这样做是为了解决我的 deepcopy
背叛问题:
l = [[0]]*3 # makes the array [[0], [0], [0]]
import JSON
m = JSON.loads(JSON.dumps(l)) # m becomes [[0], [0], [0]] with no self references
我正在寻找一种处理自共享引用的低效且不那么愚蠢的方法。
当然我不会故意制作这样的数组,但我需要处理有人将数组提供给我的代码的情况。 运行 我的 "solution" 在大型数组上速度很慢,而且我有很多级别的嵌套数组,我不能为那些野兽制作这么大的字符串。
l = [[0] for _ in range(3)]
l[0][0] = 2
print(l)
打印:
[[2], [0], [0]]
另外,在您的代码中 deepcopy()
给出了它所做的输出,因为您传入了一个列表,其中已经包含共享相同引用的元素。
from copy import deepcopy
m = deepcopy(l)
m[0][0] = 3
print(l) # prints [[2], [0], [0]]
print(m) # prints [[3], [0], [0]]
你可以在这里看到它完全符合我们的预期
不确定它是否有效,但您可以尝试:
l = [deepcopy(elt) for elt in l]
这是一种适用于列表、字典和不可变值的任意组合的方法。
def very_deep_copy(obj):
if isinstance(obj, list):
return [very_deep_copy(item) for item in obj]
elif isinstance(obj, dict):
return {k: very_deep_copy(v) for k,v in obj.items()}
else:
return obj
l = [[0]]*3
m = very_deep_copy(l)
m[0][0] = 2
print(m)
结果:
[[2], [0], [0]]
假设只有结构类型是列表,这应该适用于任意深度和复杂度的列表:
def customcopy(l):
return [customcopy(e) if type(e) is list else e for e in l]
l = [[0]]*3
x = customcopy(l)
x[0][0] = 3
>>> x
[[3], [0], [0]]
我要挑战复制共享对象才是正确做法的假设。你这么说
Of course I wouldn't make arrays like that on purpose, but I need to handle the case where someone gives one to my code.
但是,如果有人向您传递了意外共享对象的输入,则他们的代码存在错误。如果您的代码注意到错误,您的代码应该通过抛出异常告诉他们,以帮助他们修复错误。
大多数代码只是假设输入没有任何不需要的对象共享。如果你想检测它,手动遍历可能是你最好的选择,特别是因为你的输入预计是 JSON-serializable:
def detect_duplicate_references(data):
_detect_duplicate_references(data, set())
def _detect_duplicate_references(data, memo):
if isinstance(data, (dict, list)):
if id(data) in memo:
raise ValueError(f'multiple references to object {data} detected in input')
memo.add(id(data))
if isinstance(data, list):
for obj in data:
_detect_duplicate_references(obj, memo)
if isinstance(data, dict):
for obj in data.values():
_detect_duplicate_references(obj, memo)
对于一层深拷贝,你可以直接查看参考资料。对于更深的副本,只需递归执行此操作即可。
from copy import deepcopy
def copy_value(l):
l = list(l)
new_list = []
while l:
item = l.pop(0)
if item not in new_list:
new_list.append(item)
else:
new_list.append(deepcopy(item))
return new_list
l = [[0]]*3
m = copy_value(l)
m[0][0] = 2
print(m)
打印
[[2], [0], [0]]
列表理解的另一种方式:
def super_deep_copy(l):
return [i.copy() for i in l]
l = [[0]]*3
l = super_deep_copy(l)
l[0][0] = 2
现在:
print(l)
是:
[[2], [0], [0]]