Ответ 1
for row in table:
row[1:] = [int(c) for c in row[1:]]
Выше выглядит больше pythonic?
У меня есть 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-решение?
for row in table:
row[1:] = [int(c) for c in row[1:]]
Выше выглядит больше pythonic?
Try:
>>> for row in table:
... row[1:]=map(int,row[1:])
...
>>> table
[['donkey', 2, 1, 0], ['goat', 5, 3, 2]]
AFAIK, назначая срезу list
, заставляет операцию выполнять на месте вместо создания нового list
.
Мне нравится, что Шехар много отвечает.
Как правило, при написании кода 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)
Ваш "уродливый" код можно улучшить, просто позвонив 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. Существует только один очевидный способ сделать это, и это не так.
Использовать список:
table = [row[0] + [int(col) for col in row[1:]] for row in table]
Это будет работать:
table = [[row[0]] + [int(v) for v in row[1:]] for row in table]
Однако вы можете подумать о выполнении преобразования в том месте, где сначала создается список.
Выполняет то, что вы ищете. Это читаемое решение. Вы можете пойти на аналогичный, используя listcomp тоже.
>>> for row in table:
... for i, elem in enumerate(row):
... try:
... int(elem)
... except ValueError:
... pass
... else:
... row[i] = int(elem)
...