Most elegant way to modify elements of nested lists in place

前端 未结 7 717
囚心锁ツ
囚心锁ツ 2020-12-14 01:20

I have a 2D list that looks like this:

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

I want to change the la

相关标签:
7条回答
  • 2020-12-14 01:40

    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)
    
    0 讨论(0)
  • 2020-12-14 01:41

    This will work:

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

    However you might want to think about doing the conversion at the point where the list is first created.

    0 讨论(0)
  • 2020-12-14 01:45

    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.

    0 讨论(0)
  • 2020-12-14 01:54

    Use list comprehensions:

    table = [row[0] + [int(col) for col in row[1:]] for row in table]
    
    0 讨论(0)
  • 2020-12-14 01:58

    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)
    ... 
    
    0 讨论(0)
  • 2020-12-14 02:00

    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.

    0 讨论(0)
提交回复
热议问题