2017-06-27 9 views
4

これは、dict,tuplelistというオブジェクトから構築されたあらゆる種類のデータ構造から循環参照を削除する、本当にハックコードです。dicts、リスト、タプルの循環参照を削除する

import ast 

def remove_circular_refs(o): 
    return ast.literal_eval(str(o).replace("{...}", 'None')) 

しかし、私はどのようにハッキーが好きではありません。データ構造を文字列表現にすることなくこれを行うことはできますか?ここで

はしてテストするための構成例である:

doc1 = { 
    "key": "value", 
    "type": "test1", 
} 
doc1["self"] = doc1 
doc = { 
    'tags': 'Stackoverflow python question', 
    'type': 'Stackoverflow python question', 
} 
doc2 = { 
    'value': 2, 
    'id': 2, 
} 
remove_circular_refs(doc) 
remove_circular_refs(doc1) 
remove_circular_refs(doc2) 

答えて

5

なし、文字列変換を使用しないでください。ただ、データ構造を横断することによって、参照を検出:

def remove_circular_refs(ob, _seen=None): 
    if _seen is None: 
     _seen = set() 
    if id(ob) in _seen: 
     # circular reference, remove it. 
     return None 
    _seen.add(id(ob)) 
    res = ob 
    if isinstance(ob, dict): 
     res = { 
      remove_circular_refs(k, _seen): remove_circular_refs(v, _seen) 
      for k, v in ob.items()} 
    elif isinstance(ob, (list, tuple, set, frozenset)): 
     res = type(ob)(remove_circular_refs(v, _seen) for v in ob) 
    # remove id again; only *nested* references count 
    _seen.remove(id(ob)) 
    return res 

このdictlisttuplesetfrozensetオブジェクトをカバー。それぞれのオブジェクトのid()をメモし、再び見たときにはNoneに置き換えられます。

デモ:

>>> doc1 = { 
...  "key": "value", 
...  "type": "test1", 
... } 
>>> doc1["self"] = doc1 
>>> doc1 
{'key': 'value', 'type': 'test1', 'self': {...}} 
>>> remove_circular_refs(doc1) 
{'key': 'value', 'type': 'test1', 'self': None} 
>>> doc2 = { 
...  'foo': [], 
... } 
>>> doc2['foo'].append((doc2,)) 
>>> doc2 
{'foo': [({...},)]} 
>>> remove_circular_refs(doc2) 
{'foo': [(None,)]} 
>>> doc3 = { 
...  'foo': 'string 1', 'bar': 'string 1', 
...  'ham': 1, 'spam': 1 
... } 
>>> remove_circular_refs(doc3) 
{'foo': 'string 1', 'bar': 'string 1', 'ham': 1, 'spam': 1} 

最後のテストは、doc3のために、共有の参照が含まれています。 'string 1'との両方がに一度だけのように存在し、辞書にはそれらのオブジェクトへの複数の参照が含まれています。