2017-09-25 3 views
0

J'ai une question fondamentale sur la compréhension. Il y a la liste des dicts où les valeurs sont des listes, il ressemble à ceci:liste de traitement des dictionnaires - lisibilité et la complexité

listionary = [{'path': ['/tmp/folder/cat/number/letter', '/tmp/folder/hog/char/number/letter', '/tmp/folder/hog/number/letter', '/etc'], 
'mask': True, 
'name': 'dict-1'}, 
{'path': ['/tmp/folder/dog/number-2/letter-4', '/tmp/folder/hog-00/char/number-1/letter-5', '/tmp/folder/cow/number-2/letter-3'], 
'mask': True, 
'name': 'dict-2'}, 
{'path': ['/tmp/folder/dog_111/number/letter', '/tmp/folder/ant/char/number/letter', '/tmp/folder/hen/number/letter'], 
'mask': True, 
'name': 'dict-3'}] 

Ce que je dois est d'obtenir des valeurs de type liste chaque animal unique. Animal est toujours compris entre tmp/folder/ et / suivant. Ce que je l'ai fait:

import re 
flat_list = [item for sublist in [i['path'] for i in listionary] for item in sublist] 
animals = list(set([re.search('folder/([a-z]+)', elem).group(1) for elem in flat_list if 'tmp' in elem])) 

Il peut aussi être compressée en une seule ligne, mais il est assez compliqué et illisible:

animals = list(set([re.search('folder/([a-z]+)', elem).group(1) for elem in [item for sublist in [i['path'] for i in listionary] for item in sublist] if 'tmp' in elem])) 

est-il une règle d'or (par exemple zen de python) sur la taille de compréhension? Comment puis-je le rendre meilleur? Merci d'avance.

+2

I, pour un, ne suis pas amoureux de * one-liners *, parfois ils ont du sens, parfois ils ne le font pas.La lisibilité devrait être une considération. Le comprendrez-vous demain? – wwii

+0

Veuillez prendre le temps de lire [demander]. – wwii

+0

Les compréhensions sont sujettes aux mêmes lignes directrices pour les colonnes 79 ou 119 (selon les préférences), et je chercherais à les décomposer si vous commencez à les dépasser. – Aaron

Répondre

1

Comment puis-je faire mieux?

  1. demandez à quelqu'un d'autre de le lire. ✓
  2. fonctions d'utilisation pour encapsuler des opérations plus complexes
  3. ne pas imbriquer les boucles sur la même ligne

Voici comment je briser les deux derniers points ..

def get_animals(d): 
    animals = [] 
    for item in d['path']: 
     if item.startswith('/tmp/folder/'): 
      animals.append(item[12:item.find('/',12)]) 
    return animals 

animals = set() 
for d in dlist: 
    animals.update(get_animals(d)) 
animals = list(animals)
0

Vous pouvez essayer ceci:

listionary = [{'path': ['/tmp/folder/cat/number/letter', '/tmp/folder/hog/char/number/letter', '/tmp/folder/hog/number/letter', '/etc'], 
'mask': True, 
'name': 'dict-1'}, 
{'path': ['/tmp/folder/dog/number-2/letter-4', '/tmp/folder/hog-00/char/number-1/letter-5', '/tmp/folder/cow/number-2/letter-3'], 
'mask': True, 
'name': 'dict-2'}, 
{'path': ['/tmp/folder/dog_111/number/letter', '/tmp/folder/ant/char/number/letter', '/tmp/folder/hen/number/letter'], 
'mask': True, 
'name': 'dict-3'}] 
import re 
from itertools import chain 
animals = list(set(chain.from_iterable([[re.findall("/tmp/folder/(.*?)/", b)[0] for b in i["path"] if re.findall("/tmp/folder/(.*?)/", b)] for i in listionary]))) 

Sortie:

['hog', 'hog-00', 'cow', 'dog_111', 'dog', 'cat', 'ant', 'hen'] 
0

Vous pouvez le rendre plus lisible en ajoutant les nouvelles lignes et indentation. Je me suis arrêté à la ligne avec item for sublist... parce que je n'ai pas compris la logique de code, mais probablement que vous pouvez ajouter plus de nouvelles lignes là-bas.

animals = list(
    set([ 
      re.search('folder/([a-z]+)', elem).group(1) for elem in [ 
       item for sublist in [i['path'] for i in listionary] for item in sublist 
      ] 
      if 'tmp' in elem 
    ]) 
) 

Cela dit, je considère quelque chose comme ça à être beaucoup plus lisible:

def animal_name_from_path(path): 
    return re.search('folder/([a-z]+)', path).group(1) 

def is_animal_path(path): 
    return '/tmp' in path 

def deduplicate(L): 
    return list(set(L)) 

path_list = [] 
for item in listionary: 
    path_list.extend(item['path']) 

animals = deduplicate([animal_name_from_path(path) for path in path_list if is_animal_path(path)]) 

Une règle appliquée ici est que tout concept devrait avoir un nom. Dans votre code d'origine, item for sublist in [i['path'] for i in listionary] for item in sublist est très difficile à comprendre car on ne sait pas ce que sont supposés être item et i. Dans ce nouveau bloc, il est beaucoup plus clair que vous êtes en train d'aplatir une liste de chemins. Le code d'identification du nom d'animal est beaucoup plus facile à comprendre une fois que tous les concepts sont nommés. Ici, j'ai peut-être pris à l'extrême - vous pouvez trouver votre propre équilibre heureux que vous trouvez le plus lisible.

0

solution raccourcies:

animals = set(re.search(r'/folder/([a-z]+)', p).group(1) for d in listionary for p in d['path'] if '/tmp' in p) 
print(animals) 

La sortie:

{'hog', 'cat', 'dog', 'cow', 'hen', 'ant'}