2017-06-05 2 views
1

J'ai besoin d'un itérateur sur toutes les feuilles de mon objet JSON. J'ai donc écrit cette fonctionComment céder les éléments dans un itérateur dans un nouvel itérateur?

rec = {'a': {'b': [{'c': {'d': [{'e': 'x1','f': 'x2'}],'g': 'x3'}}],'h': 'x4','i': 'x5','j': [{'k': 'x6'}],'l': [{'m': {'n': 'x7'}}]}} 

def yield_leaves(rec, lbl = ''): 
    if isinstance(rec, dict): 
    for key, value in rec.items(): 
     for to_yield in yield_leaves(value, key): 
      yield to_yield 
    if isinstance(rec, list): 
    for value in rec: 
     for to_yield in yield_leaves(value, lbl): 
      yield to_yield 
    if isinstance(rec, (int, str)): 
    for entry in rec.split(): 
     yield entry, lbl 

print(list(yield_leaves(rec))) 
>>> [('x5', 'i'), ('x4', 'h'), ('x1', 'e'), ('x2', 'f'), ('x3', 'g'), ('x6', 'k'), ('x7', 'n')] 

Mais je pense qu'un code est redondant. La ligne suivante

for to_yield in yield_leaves(value, key): 
     yield to_yield 

Il itère sur un itérateur et renvoie les valeurs dans le cadre d'un itérateur.

Connaissez-vous un moyen plus efficace de coder cela?

+2

'rendement de yield_leaves (valeur, clé)' sur Python 3 –

Répondre

1

Votre section

if isinstance(rec, (int, str)): 
    for entry in rec.split(): 
     yield entry, lbl 

est un peu étrange. Il se plantera si rec est un int, puisque les entiers n'ont pas de méthode .split. Et aucune de vos chaînes ne contient d'espaces, donc l'appel .split sur eux va juste retourner une liste contenant un seul élément: la chaîne d'origine. Je suppose que vos données réelles peuvent contenir des chaînes de plusieurs mots que vous souhaitez diviser, mais si c'est le cas, vous devez vraiment gérer cela séparément de int s.

Donc, en supposant que vous n'avez pas ont des valeurs de multi-mots que vous souhaitez diviser, j'ai simplifié votre code un peu. Comme vous pouvez le voir, je n'ai retenu les essais dict et list, puisque les données sont décodées à partir JSON, tout autre type rec sera une sorte de scalaire: int, str, bool ou None, (sauf si vous avez créé un décodage personnalisé), et nous pouvons gérer tous ces types scalaires de manière identique.

rec = { 
    'a': { 
     'b': [ 
      { 
       'c': { 
        'd': [{'e': 'x1', 'f': 'x2'}], 
        'g': 'x3' 
       } 
      } 
     ], 
     'h': 'x4', 
     'i': 'x5', 
     'j': [{'k': 'x6'}], 
     'l': [{'m': {'n': 'x7'}}] 
    } 
} 

def yield_leaves(rec, lbl=''): 
    if isinstance(rec, dict): 
     for key, value in rec.items(): 
      yield from yield_leaves(value, key) 
    elif isinstance(rec, list): 
     for value in rec: 
      yield from yield_leaves(value, lbl) 
    else: 
     yield rec, lbl 

print(list(yield_leaves(rec))) 

sortie

[('x1', 'e'), ('x2', 'f'), ('x3', 'g'), ('x4', 'h'), ('x5', 'i'), ('x6', 'k'), ('x7', 'n')] 

Ce code utilise Python 3 fonction yield from; Si vous n'utilisez pas Python 3, vous devriez l'être. :)

+0

Oui, '' 'yield from''' fait ce que je cherchais. Merci. –

+0

Et vous avez raison d'appeler split sur les entiers. Mauvais copier-coller de mon côté –

+0

@RobRomijnders Pas de soucis. Si ma réponse vous a aidé, veuillez considérer [accepter] (http://meta.stackexchange.com/a/5235). –

1

Essayez d'utiliser le rendement du lieu de pour

yeild from yield_leaves(value, key) 

post-scriptum Si vous utilisez python> 3.3

+0

Oui, '' '' yield from''' fait l'affaire! –