我有一个数组集合,这些数组在某些元素上“重叠”。这是涉及3个字符数组的示例的图片:
array0↓
'A' ↓array2
array1→'B' 'D' 'E'
'C' 'F'
重要的是,对数组的更改应遵循此结构。因此,例如,如果我将array0中的“ B”更改为“ X”,则array1中的“ B”也应更改为“ X”。
我的问题是,用Python实现此功能的一种好而有效的方法是什么?
到目前为止,我已经想到了两件事:
一个,我可以创建一个定制的类,该类的实例包含一个完全不同的列表,以及有关其具有的任何重叠的信息,并适当地实现更新方法,以使对该列表的任何更改始终对其他列表重复在重叠处。不过,这似乎有些不合时宜,并且涉及到重复数据。
两个,我可以通过使用像这样的单例列表来做到这一点:
data = [['A'], ['B'], ['C'], ['D'], ['E'], ['F']]
array0 = [data[0], data[1], data[2]]
array1 = [data[1], data[3], data[4]]
array2 = [data[4], data[5]]
for array in array0, array1, array2:
print(array)
>>> [['A'], ['B'], ['C']]
>>> [['B'], ['D'], ['E']]
>>> [['E'], ['F']]
array0[1][0] = 'X'
for array in array0, array1, array2:
print(array)
>>> [['A'], ['X'], ['C']]
>>> [['X'], ['D'], ['E']]
>>> [['E'], ['F']]
但是我觉得这可能很棘手,而不是最好的方法。感谢您的任何建议。
答案 0 :(得分:2)
我的建议是@a_guest提出的建议的变体。您可以有一个包装器类,将元素标记为共享,并提供一个用于处理此类元素的数据结构:
class SharedElement:
def __init__(self, val):
self.val = val
def update(self, val):
self.val = val
def __repr__(self):
return "SharedElement({0})".format(self.val)
def __str__(self):
return str(self.val)
class SharedList:
def __init__(self, lst):
self._lst = lst
def __getitem__(self, item):
if isinstance(self._lst[item], SharedElement):
return self._lst[item].val
return self._lst[item]
def __setitem__(self, key, value):
if isinstance(self._lst[key], SharedElement):
self._lst[key].update(value)
B = SharedElement('B')
E = SharedElement('E')
a = SharedList(['A', B, 'C'])
b = SharedList([B, 'D', E])
c = SharedList([E, 'F'])
b[0] = 'X'
print([val for val in a])
print([val for val in b])
print([val for val in c])
输出
['A', 'X', 'C']
['X', 'D', 'E']
['E', 'F']
答案 1 :(得分:1)
您可以创建一个包装器类,以处理具有相同值的所有元素的更新:
arr = [[['A'], ['B'], ['C']], [['B'], ['D'], ['E']], [['E'], ['F']]]
class WrapArray:
def __init__(self, _data):
self.d = _data
def __getitem__(self, _x):
self.x = _x
class _wrapper:
def __init__(self, _inst):
self.ref = _inst
def __setitem__(self, _y, _val):
_place = self.ref.d[self.ref.x][_y][0]
self.ref.d[self.ref.x][_y][0] = _val
for i in range(len(self.ref.d)):
for b in range(len(self.ref.d[i])):
if self.ref.d[i][b][0] == _place:
self.ref.d[i][b] = [_val]
return _wrapper(self)
def __repr__(self):
return str(self.d)
array = WrapArray(arr)
array[1][0] = 'X'
输出:
[[['A'], ['X'], ['C']], [['X'], ['D'], ['E']], [['E'], ['F']]]
答案 2 :(得分:1)
您可以使用一个专用类,该类可以按照您的第一个想法指示适当地更新其他相交实例。我不会认为数据重复是一个问题,因为无论如何对于可变数据,您都应该存储引用,并且如果您使用大型不可变数据,则可以采用专用的包装器类(例如Python 3.7引入了@dataclass
装饰器)。
这是一个示例实现:
from collections import defaultdict
class List(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._intersections = defaultdict(list)
def __setitem__(self, index, value):
super().__setitem__(index, value)
for i, other in self._intersections[index]:
other[i] = value
def intersect(self, other, at):
self._intersections[at[0]].append((at[1], other))
因此,您可以像示例中那样将列表相交:
a = List(['A', 'B', 'C'])
b = List(['B', 'D', 'E'])
c = List(['E', 'F'])
a.intersect(b, (1, 0))
b.intersect(c, (2, 0))
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
哪个作为输出:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
答案 3 :(得分:1)
您可以继承list
的子类,并使用专用的包装器类来代理共享的内容。这不涉及数据重复,因为它仅存储分派到原始数据的共享数据的代理。这有点类似于您的嵌套列表方法,但它保持了常规列表接口。这是一个示例实现:
class Intersection:
def __init__(self, other, index):
self.other = other
self.index = index
def __repr__(self):
return repr(self.other[self.index])
@property
def value(self):
return self.other[self.index]
@value.setter
def value(self, v):
self.other[self.index] = v
class List(list):
def __getitem__(self, index):
item = super().__getitem__(index)
return item.value if isinstance(item, Intersection) else item
def __setitem__(self, index, value):
item = super().__getitem__(index)
if isinstance(item, Intersection):
item.value = value
else:
super().__setitem__(index, value)
def share(self, index):
return Intersection(self, index)
现在,您可以根据需要在列表之间共享数据:
a = List(['A', 'B', 'C'])
b = List([a.share(1), 'D', 'E'])
c = List([b.share(2), 'F'])
a[1] = 'X'
b[2] = 'Y'
print(a)
print(b)
print(c)
哪个作为输出:
['A', 'X', 'C']
['X', 'D', 'Y']
['Y', 'F']
答案 4 :(得分:0)
您在问题中指出,相关信息是
array0ptr = [0, 1, 2]
array1ptr = [1, 3, 4]
array2ptr = [4, 5]
(我添加了后缀ptr,因为实际上那些元素是指针)。 这里的list元素是指向要维护的对象的指针 在单独的列表中
ol = ['A', 'B', 'C', 'D', 'E']
真正的数组可以在运行时通过成员函数(如p
)获得array0 = []
for i in range(len(array0ptr)):
array0.append(ol[array0ptr[i]])
现在,您的观点是:假设对象列表变为
ol = ['A', 'B', 'intruder', 'C', 'D', 'E']
如何自动在阵列中保持跟踪?这些数组应该变成:
array0ptr = [0, 1, 3]
array1ptr = [1, 4, 5]
array2ptr = [5, 6]
我认为最简单的答案是:保持列表固定! 不允许插入或更改项目顺序。只需维护 与对象位置不同的哈希值。在上述情况下,您将拥有
sl = ['A', 'B', 'C', 'D', 'E', 'intruder']
slorder = [0, 1, 3, 4, 5, 2]
然后可以编写成员函数来转储更新的对象列表, 数组不会改变。可能很棘手的是是否要删除对象,但是无论如何我都担心这很棘手。