Подтвердить что ты не робот

Самый элегантный способ изменения элементов вложенных списков

У меня есть 2D-список, который выглядит так:

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

Я хочу изменить последние три элемента на целые числа, но приведенный ниже код кажется очень уродливым:

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

Но я бы предпочел бы что-то вроде:

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

Я думаю, что должен быть способ написать выше код, но срез создает итератор/новый список, который отделен от оригинала, поэтому ссылки не переносятся.

Есть ли способ получить более Pythonic-решение?

4b9b3361

Ответ 1

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

Выше выглядит больше pythonic?

Ответ 2

Try:

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

AFAIK, назначая срезу list, заставляет операцию выполнять на месте вместо создания нового list.

Ответ 3

Мне нравится, что Шехар много отвечает.

Как правило, при написании кода Python, если вы обнаруживаете, что пишете f or i in range(len(somelist)), вы делаете это неправильно:

  • попробуйте enumerate, если у вас есть один список
  • попробуйте zip или itertools.izip, если у вас есть 2 или более списков, которые вы хотите повторить параллельно.

В вашем случае первый столбец отличается тем, что вы не можете элегантно использовать перечисление:

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

Ответ 4

Ваш "уродливый" код можно улучшить, просто позвонив range с двумя аргументами:

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

Это, пожалуй, самое лучшее, что вы можете сделать, если настаиваете на изменении элементов на месте, не выделяя новые временные списки (используя понимание списка, map и/или нарезку). См. Есть ли эквивалент "map" на месте в python?

Хотя я не рекомендую его, вы также можете сделать этот код более общим, представив свою собственную функцию карты на месте:

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)

Лично я нахожу это менее Pythonic. Существует только один очевидный способ сделать это, и это не так.

Ответ 5

Использовать список:

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

Ответ 6

Это будет работать:

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

Однако вы можете подумать о выполнении преобразования в том месте, где сначала создается список.

Ответ 7

Выполняет то, что вы ищете. Это читаемое решение. Вы можете пойти на аналогичный, используя listcomp тоже.

>>> for row in table:
...     for i, elem in enumerate(row):
...             try:
...                     int(elem)
...             except ValueError:
...                     pass
...             else:
...                     row[i] = int(elem)
...