web-dev-qa-db-fra.com

plusieurs variables dans la compréhension de liste?

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()])
5
Dave

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.

7
kindall

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.

1
yper

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])
1
user2314737

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']]
1
Dave

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'
0
shaktimaan