Je veux créer une liste de listes à partir d'une liste de chaînes multi-champs et me demande s'il est possible de le faire avec compréhension.
Contribution:
inputs = ["1, foo, bar", "2, tom, jerry"]
Sortie désirée:
[[1, "foo", "bar"], [2, "tom", "jerry"]]
Fractionner la ficelle en une compréhension est facile:
>>> [s.split(",") for s in inputs]
[['1', ' foo', ' bar'], ['2', ' tom', ' jerry']]
Mais j'ai du mal à comprendre comment accéder aux colonnes une fois que la chaîne a été scindée à l'intérieur de la compréhension, car cela semblerait nécessiter une affectation de variable. Les éléments suivants ne sont pas valides en Python, mais illustrent ce que je recherche:
[[int(x), y.strip(), z.strip() for x,y,z = s.split(",")] for s in inputs]
or
[[int(v[0]), v[1].strip(), v[2].strip() for v = s.split(",")] for s in inputs]
Existe-t-il un moyen d'affecter des variables dans une compréhension de sorte que le résultat puisse être composé de fonctions des variables? Une boucle est triviale, mais générer une liste en transformant des entrées semble être une tâche de «compréhension».
outputs = []
for s in inputs:
x,y,z = s.split(",")
outputs.append([int(x), y.strip(), z.strip()])
Vous pouvez le faire avec deux clauses for
dans votre liste de compréhension. La première itère sur les éléments de la liste. La seconde itère sur une liste à élément unique contenant la liste dérivée de la division de la chaîne (ce qui est nécessaire pour pouvoir la décompresser en trois variables distinctes).
[[int(x), y.strip(), z.strip()] for s in inputs for (x, y, z) in [s.split(",")]]
Les clauses for
vont dans un ordre quelque peu contre-intuitif, mais elles correspondent à la façon dont vous l'écriviez sous la forme de boucles for
imbriquées.
L'utilisation par Jon Sharpe d'une compréhension imbriquée (expression génératrice, en fait) est similaire et probablement plus claire. L'utilisation de plusieurs clauses for
me semble toujours déroutante; principalement je voulais voir si je pouvais en faire usage ici.
Si vous voulez vraiment le faire en une seule ligne, vous pouvez faire quelque chose comme ceci, bien que ce ne soit pas le code le plus clair (la première ligne est le code, la deuxième ligne est la sortie).
>>> [(lambda x, y, z: [int(x), y.strip(), z.strip()])(*s.split(",")) for s in inputs]
[[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
Ou ca.
>>> [(lambda x: [int(x[0]), x[1].strip(), x[2].strip()])(s.split(",")) for s in inputs]
[[1, 'foo', 'bar'], [2, 'tom', 'jerry']
Edit: Voir le commentaire de jonrsharpe pour la meilleure réponse à mon humble avis.
Vous pouvez utiliser la carte avec une compréhension de liste
def clean(x):
return [int(x[0]), x[1].strip(), x[2].strip()]
map(clean,[s.split(",") for s in inputs])
# Output: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
avec une fonction de lambda:
map(lambda x: [int(x[0]), x[1].strip(), x[2].strip()],[s.split(",") for s in inputs])
Merci pour toutes les suggestions - c'est un plaisir de voir la variété de techniques possibles, ainsi qu'un contre-exemple au zen de Python "Il devrait y avoir un - et de préférence un seul - moyen évident de le faire."
Les 4 solutions sont toutes aussi belles, il est donc un peu injuste de donner le chèque vert convoité à une seule d'entre elles. Je suis d'accord avec les recommandations selon lesquelles # 1 est l'approche la plus propre et la meilleure. # 2 est également simple à comprendre, mais devoir utiliser un lambda dans une compréhension semble un peu louche. # 3 est agréable à créer un itérateur avec map mais obtient un petit démérite pour avoir besoin de l'étape supplémentaire d'itérer dessus. # 4 est cool pour indiquer que les imbriqués sont possibles - si je me souviens bien, ils vont dans l'ordre "premier, deuxième" au lieu de "intérieur, extérieur". Puisque n ° 1 n’est pas sous forme de réponse, n ° 4 obtient le chèque le plus surprenant.
Merci encore à tous.
inputs = ["1, foo, bar", "2,tom, jerry"]
outputs1 = [[int(x), y.strip(), z.strip()] for x,y,z in (s.split(',') for s in inputs)]
print("1:", outputs1) # jonrsharpe
outputs2 = [(lambda x, y, z: [int(x), y.strip(), z.strip()])(*s.split(",")) for s in inputs]
print("2:", outputs2) # yper
outputs3 = [z for z in map(lambda x: [int(x[0]), x[1].strip(), x[2].strip()],[s.split(",") for s in inputs])]
print("3:", outputs3) # user2314737
outputs4 = [[int(x), y.strip(), z.strip()] for s in inputs for (x, y, z) in [s.split(",")]]
print("4:", outputs4) # kindall
Résultats:
1: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
2: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
3: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
4: [[1, 'foo', 'bar'], [2, 'tom', 'jerry']]
Une autre solution possible
>>> inputs = [[1, "foo", "bar"], [2, "tom", "jerry"]]
>>> list_of_dicts = [{"var{}".format(k): v for k, v in enumerate(s, start=1)} for s in inputs]
>>> list_of_dicts[0]['var1']
1
>>> list_of_dicts[0]['var2']
'foo'