1
votes

Objets Pickle avec générateurs imbriqués

Supposons que j'ai une liste imbriquée arbitrairement dans laquelle certains des éléments imbriqués peuvent être des générateurs. Par exemple:

[
    [1, [2, [3, 4]]],
    [2, [1, 2, 3]],
    [3, [['0'], ['0', '1']]],
    {'a': [{'x': [0.0, 1.0]}, {'y': [0.0, 1.0]}, {'z': [0.0, 1.0]}]},
    {'b': {'c': {0: 'a', 1: 'b', 2: 'c'}}}
]

Comment puis-je parcourir cette structure de manière récursive et consommer tous les objets générateurs afin qu'ils puissent être picklés?

Ma sortie souhaitée est: p >

nested_gens = [
    [1, [2, [3, 4]]],
    [2, (map(int, '123'))],
    [3, (map(str, range(i+1)) for i in range(2))],
    {'a': ({k: (float(i) for i in range(2))} for k in 'xyz')},
    {'b': {'c': dict(zip(range(3), 'abc'))}}
]

Une solution à cette question pourrait être généralisée pour le décapage des objets contenant des générateurs. Toutes les réponses que j'ai trouvées pour traiter TypeError: impossible de décaper les objets du générateur ne concernent pas les générateurs imbriqués.

Mise à jour : La solution doit être capable de gérer des éléments imbriqués de tout type.


0 commentaires

3 Réponses :


1
votes

Une façon est de parcourir l'objet imbriqué de manière récursive et de transformer les générateurs en listes s.

print(consume_all_generators(nested_gens))
#[[1, [2, [3, 4]]],
# [2, [1, 2, 3]],
# [3, [['0'], ['0', '1']]],
# {'a': [{'x': [0.0, 1.0]}, {'y': [0.0, 1.0]}, {'z': [0.0, 1.0]}]},
# {'b': {'c': {0: 'a', 1: 'b', 2: 'c'}}}]

Appliquer ceci à l'exemple de la question:

from inspect import isgenerator, isgeneratorfunction

def consume_all_generators(row):

    if isinstance(row, str):
        return row
    elif isinstance(row, dict):
        return {k: consume_all_generators(v) for k, v in row.items()}

    output = []
    try:
        for val in row:
            if isgenerator(val) or isgeneratorfunction(val):
                output.append(list(consume_all_generators(val)))
            else:
                output.append(consume_all_generators(val))
        return output
    except TypeError:
        return row


0 commentaires

1
votes

Vous pouvez également utiliser une compréhension de liste en récursivité pour une solution sans importation:

[[1, [2, [3, 4]]], [2, [1, 2, 3]], [3, [['0'], ['0', '1']]], {'a': [{'x': [0.0, 1.0]}, {'y': [0.0, 1.0]}, {'z': [0.0, 1.0]}]}, {'b': {'c': {0: 'a', 1: 'b', 2: 'c'}}}]

nested_gens = [
  [1, [2, [3, 4]]],
  [2, (map(int, '123'))],
  [3, (map(str, range(i+1)) for i in range(2))],
  {'a': ({k: (float(i) for i in range(2))} for k in 'xyz')},
  {'b': {'c': dict(zip(range(3), 'abc'))}}
]
print(_test(nested_gens))

Résultat:

def _test(d):
  if isinstance(d, str):
    return d
  try:
    _l = [i for i in d]
    return [_test(i) for i in _l] if not isinstance(d, dict) else {a:_test(b) for a, b in d.items()}
  except:
    return d


0 commentaires

0
votes

Essayez ceci

   
def consume(g):
    if hasattr(g, '__iter__') and not isinstance(g, str):
        if isinstance(g, tuple):
            return (consume(e) for e in g)
        elif isinstance(g, dict):
            return { k: consume(v) for k,v in g.items()}
        else:
            return [consume(e) for e in g]
    return g

nested_gens = [ [1, [2, [3, 4]]], 
    [2, (map(int, '123'))], 
    [3, (map(str, range(i+1)) for i in range(2))], 
    {'a': ({k: (float(i) for i in range(2))} for k in 'xyz')},
    {'b': {'c': dict(zip(range(3), 'abc'))}} ]
print(consume(nested_gens))


0 commentaires