Most elegant way to modify elements of nested list

2019-01-22 06:36发布

I have a 2D list that looks like this:

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

I want to change the last three elements to integers, but the code below feels very ugly:

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

But I'd rather have something that looks like:

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

I think there should be a way to write the code above, but the slice creates an iterator/new list that's separate from the original, so the references don't carry over.

Is there some way to get a more Pythonic solution?

7条回答
时光不老,我们不散
2楼-- · 2019-01-22 07:00

Use list comprehensions:

table = [row[0] + [int(col) for col in row[1:]] for row in table]
查看更多
beautiful°
3楼-- · 2019-01-22 07:12
for row in table:
    row[1:] = [int(c) for c in row[1:]]

Does above look more pythonic?

查看更多
我只想做你的唯一
4楼-- · 2019-01-22 07:13

Try:

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

AFAIK, assigning to a list slice forces the operation to be done in place instead of creating a new list.

查看更多
相关推荐>>
5楼-- · 2019-01-22 07:13

I like Shekhar answer a lot.

As a general rule, when writing Python code, if you find yourself writing for i in range(len(somelist)), you're doing it wrong:

  • try enumerate if you have a single list
  • try zip or itertools.izip if you have 2 or more lists you want to iterate on in parallel

In your case, the first column is different so you cannot elegantly use enumerate:

for row in table:
    for i, val in enumerate(row):
        if i == 0: continue
        row[i] = int(val)
查看更多
啃猪蹄的小仙女
6楼-- · 2019-01-22 07:17

Your "ugly" code can be improved just by calling range with two arguments:

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

This is probably the best you can do if you insist on changing the items in place without allocating new temporary lists (either by using a list comprehension, map, and/or slicing). See Is there an in-place equivalent to 'map' in python?

Although I don't recommend it, you can also make this code more general by introducing your own in-place map function:

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)

Personally, I find this less Pythonic. There is preferably only one obvious way to do it, and this isn't it.

查看更多
【Aperson】
7楼-- · 2019-01-22 07:22

This accomplishes what you are looking for. It is a readable solution. You can go for similar one using listcomp too.

>>> for row in table:
...     for i, elem in enumerate(row):
...             try:
...                     int(elem)
...             except ValueError:
...                     pass
...             else:
...                     row[i] = int(elem)
... 
查看更多
登录 后发表回答