Удалить общие ссылки в списке-списке? - PullRequest
0 голосов
/ 31 января 2019

Хорошо, позвольте мне объяснить проблему на простом примере:

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

Я ищу менее неэффективный и менее глупый способ обработки собственных ссылок.

Конечно, я бы не сталНе делайте массивы подобным образом специально, но мне нужно разобраться со случаем, когда кто-то отдает один мой код.Выполнение моего «решения» на больших массивах происходит медленно, и у меня есть много уровней вложенных массивов, я не могу позволить себе сделать строку такой большой для этих зверей.

Ответы [ 7 ]

0 голосов
/ 31 января 2019

Для одноуровневой копии вы можете просто проверить ссылки.Для более глубоких копий просто сделайте это рекурсивно.

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]]
0 голосов
/ 01 февраля 2019

Другой способ с пониманием списка:

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)

Is:

[[2], [0], [0]]
0 голосов
/ 31 января 2019

Я собираюсь опровергнуть предположение, что правильное решение - копировать общие объекты.Вы говорите, что

Конечно, я бы не создавал массивы подобным образом специально, но мне нужно разобраться со случаем, когда кто-то дает его моему коду.

, ноесли кто-то передает вам ввод с неожиданным совместным использованием объекта, в его коде есть ошибка.Если ваш код замечает ошибку, ваш код должен сообщить им об этом, сгенерировав исключение , чтобы помочь им исправить свои ошибки.

Большая часть кода просто предполагает, что входные данные не имеютсовместное использование нежелательных объектов.Если вы все равно хотите его обнаружить, то, вероятно, лучшим вариантом будет ручной обход, тем более что ожидается, что ваш ввод будет JSON-сериализуемый:

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)
0 голосов
/ 31 января 2019

Предполагая, что только тип структуры будет списком, это должно работать для списка произвольной глубины и сложности:

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]]
0 голосов
/ 31 января 2019

Вот подход, который будет работать с любой комбинацией списков, диктов и неизменных значений.

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]]
0 голосов
/ 31 января 2019

Не уверен, что это эффективно, но вы можете попробовать:

l = [deepcopy(elt) for elt in l]
0 голосов
/ 31 января 2019
l = [[0] for _ in range(3)]
l[0][0] = 2
print(l)

печатает:

[[2], [0], [0]]

также, кстати в вашем коде deepcopy() дал вывод, который он сделал, потому что вы передали список, в котором уже есть элементы, которые разделяютта же ссылка

...