web-dev-qa-db-fra.com

Manière la plus élégante de modifier des éléments de listes imbriquées en place

J'ai une liste en 2D qui ressemble à ceci:

table = [['donkey', '2', '1', '0'], ['goat', '5', '3', '2']]

Je veux changer les trois derniers éléments en entiers, mais le code ci-dessous est très moche:

for row in table:
    for i in range(len(row)-1):
        row[i+1] = int(row[i+1])

Mais je préfère avoir quelque chose qui ressemble à:

for row in table:
    for col in row[1:]:
        col = int(col)

Je pense qu'il devrait y avoir un moyen d'écrire le code ci-dessus, mais la tranche crée un itérateur/une nouvelle liste qui est distinct de l'original, afin que les références ne soient pas reportées. 

Y a-t-il un moyen d'obtenir une solution plus Pythonic?

28
Conrad.Dean
for row in table:
    row[1:] = [int(c) for c in row[1:]]

Est-ce que ci-dessus semble plus pythonique?

20
Shekhar

Essayer:

>>> for row in table:
...     row[1:]=map(int,row[1:])
... 
>>> table
[['donkey', 2, 1, 0], ['goat', 5, 3, 2]]

D'après les informations dont je dispose, l'attribution d'une tranche list force l'opération à effectuer sur place au lieu de créer une nouvelle variable list.

13
MAK

J'aime beaucoup Shekhar répondre. 

En règle générale, lorsque vous écrivez du code Python, si vous vous trouvez en train d'écrire for i in range(len(somelist)), vous le faites mal:

  • essayez enumerate si vous avez une seule liste
  • essayez Zip ou itertools.izip si vous souhaitez parcourir deux ou plusieurs listes en parallèle.

Dans votre cas, la première colonne est différente et vous ne pouvez donc pas utiliser élégamment énumérer:

for row in table:
    for i, val in enumerate(row):
        if i == 0: continue
        row[i] = int(val)
8
gurney alex

Votre code "moche" peut être amélioré en appelant simplement range avec deux arguments:

for row in table:
    for i in range(1, len(row)):
        row[i] = int(row[i])

C'est probablement ce que vous pouvez faire de mieux si vous insistez pour changer les éléments en place sans allouer de nouvelles listes temporaires (en utilisant une compréhension de liste, map et/ou un découpage en tranches). Voir Existe-t-il un équivalent sur place de "carte" en python?

Bien que je ne le recommande pas, vous pouvez également généraliser ce code en introduisant votre propre fonction de carte in-situ:

def inplacemap(f, items, start=0, end=None):
    """Applies ``f`` to each item in the iterable ``items`` between the range
    ``start`` and ``end``."""
    # If end was not specified, make it the length of the iterable
    # We avoid setting end in the parameter list to force it to be evaluated on
    # each invocation
    if end is None:
        end = len(items)
    for i in range(start, end):
        items[i] = f(items[i])

for row in table:
    inplacemap(int, row, 1)

Personnellement, je trouve cela moins Pythonic. Il n'y a de préférence qu'une seule façon évidente de le faire, et ce n'est pas ça.

3
Wesley

Utiliser les compréhensions de liste:

table = [row[0] + [int(col) for col in row[1:]] for row in table]
1
Nicholas Mancuso

Cela fonctionnera:

table = [[row[0]] + [int(v) for v in row[1:]] for row in table]

Cependant, vous voudrez peut-être songer à effectuer la conversion au moment où la liste est créée pour la première fois.

0
Keith