web-dev-qa-db-ja.com

リストの共有参照を削除しますか?

わかりました。簡単な例で問題を説明しましょう。

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

私は、自己共有参照を処理するための非効率的で愚かな方法を探しています。

もちろん、わざとそのような配列を作ることはしませんが、誰かが私のコードに配列を与えた場合に対処する必要があります。大きな配列で「ソリューション」を実行するのは遅く、ネストされた配列のレベルがたくさんあるので、それらの獣のために文字列をこれほど大きくする余裕はありません。

18
Benoît Pilatte

これは、リスト、dict、および不変の値の任意の組み合わせで機能するアプローチです。

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]]
10
Kevin

共有オブジェクトをコピーすることが正しいことであるという仮定に異議を唱えます。あなたはそれを言う

もちろん、わざとそのような配列を作ることはしませんが、誰かが私のコードに配列を与えた場合に対処する必要があります。

しかし、誰かが予期しないオブジェクト共有で入力を渡した場合、そのコードにはバグがあります。コードがバグに気付いた場合、コードはバグの修正を支援するために例外をスローするによってバグを通知する必要があります。

ほとんどのコードは、入力に不要なオブジェクト共有がないことを前提としています。とにかくそれを検出したい場合は、特に入力が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)
_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]]
_

あなたはここでそれが問題なくそれを期待することをするのを見ることができます

2
sam46

構造タイプのみがリストになると仮定すると、これは任意の深さと複雑さのリストに対して機能するはずです。

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]]
1
Rocky Li

1レベルの深さのコピーの場合は、参照を確認するだけです。より深いコピーの場合は、これを再帰的に実行します。

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
Tim

リスト内包表記の別の方法:

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]]
0
U10-Forward

効率的かどうかはわかりませんが、次のことを試すことができます。

l = [deepcopy(elt) for elt in l]