I wrote this function to fill a list with object based on the desired index they should have.

Here the func:

def fill(lst, index, add, fillvalue=None):                                        
    '''Fills a list with the add value based on desired index'''                       
    '''Let the value in place if present'''                                                            
    for n in range(index):                                                        
            if lst[n]:                                                            
        except Exception:                                                         
        if lst[index]:                                                            
    except Exception:                                                             
        if index==len(lst):                                                       
            #This if check probably not mandatory                                               
    return lst

edit: here's an usage example that would for example happen in a for loop

>>> my_list = [1, 7, 14]
>>> fill(my_list, 5, 6)
[1, 7, 14, None, None, 6]
>>> fill(my_list, 6, 12)
[1, 7, 14, None, None, 6, 12]
>>> fill(my_list, 3, 25)
[1, 7, 14, 25, None, 6, 12]
>>> fill(my_list, 4, 18)
[1, 7, 14, 25, 18, 6, 12]


>>> my_list = [1, 7, 14]
>>> fill(my_list, 1, 6)
#returns the list not modified, my_list[1] is already taken
[1, 7, 14]

It works good for my purpose (matrix generating). My matrixes are generated with many "for loops" passes, because the data I convert to is unordered. This is why the "None" elements are important in order to fill them out on a subsequent loop pass.

My questions are:

  • I'm wondering if I made everything as simple as possible? Or if there were a lib, that would do the same job?

  • For now the matrixes aren't so long but I guess, if I go further in my development, they will become big (1000+ lines). Should I somehow better use itertools?

  • I also somehow thought of first using a dict {index: value} and convert it to a list when filled up. Would it be better practice?

Edit: Now that I pasted the code in here I notice the arg name "add" is blued. My Vim-editor didn't told me that... Is it a name already taken by the python core?

  • \$\begingroup\$ Hey, welcome to Code Review! Can you add a usage example? it is not quite clear to me how to use your function properly and what exactly it should do. \$\endgroup\$
    – Graipher
    Commented Mar 12, 2020 at 14:43
  • \$\begingroup\$ @Graipher Thank you ! I edited the question with an example. \$\endgroup\$
    – Halavus
    Commented Mar 12, 2020 at 15:01
  • \$\begingroup\$ Using except Exception: like that is a bad idea, see stackoverflow.com/questions/54948548/…. \$\endgroup\$
    – AMC
    Commented Mar 13, 2020 at 23:03

1 Answer 1

  • if lst[n] is not sufficient to determine if the value at index n is None, 0 will also not pass the test. Use if lst[n] is not None or if lst[n] is None, depending on which one you want.

  • The whole first loop is just trying to determine if you need to extend the list, but you could just do that:

    lst.extend([fill_value] * (index - len(lst) + 1))

    If the list is already long enough this does nothing, as a list multiplied by an integer smaller than one is the empty list, and extending with an empty list does nothing. It even works when you use negative indices.

  • This also guarantees that the index exists, so you don't need the try..except anymore. But if you use try and except, always try to catch the narrowest exception possible. This way you make sure you don't catch an unexpected exception. In this case it would be an IndexError instead of the more generic Exception.

  • The convention in Python is for functions to only do one of two things: either do not mutate the inputs and return a new output or mutate (some of) the input and return None (potentially implicitly). Your function falls into the second category.

With these changes your function could be a lot shorter:

def fill(lst, index, value, fill_value=None):
    """Place the `value` at index `index`,
    but only if the value already there is `None`.
    Extends the list with `fill_value`, if necessary.
    # No-op if list long enough or index negative
    lst.extend([fill_value] * (index - len(lst) + 1))
    if lst[index] is None:
        lst[index] = value

my_list = [1, 7, 14]
# my_list = [1, 7, 14]
fill(my_list, 5, 6)
# my_list = [1, 7, 14, None, None, 6]
fill(my_list, 6, 12)
# my_list = [1, 7, 14, None, None, 6, 12]
fill(my_list, 3, 25)
# my_list = [1, 7, 14, 25, None, 6, 12]
fill(my_list, 4, 18)
# my_list = [1, 7, 14, 25, 18, 6, 12]

Note that you will not directly see the result anymore in an interactive session, since the list is no longer returned. But that's a feature, not a bug, as mentioned above.

Of course, if you need to do this multiple times directly after each other, you might want to define a function that needs to extend only once:

def fill_multiple(lst, indices, values, fill_value=None):
    lst.extend([fill_value] * (max(indices) - len(lst) + 1))
    for index, value in zip(indices, values):
        if lst[index] is None:
            lst[index] = value

Then you can use e.g a dictionary:

my_list = [1, 7, 14]
updates = {5: 6, 6: 12, 3: 25, 4: 18}
fill_multiple(my_list, updates.keys(), updates.values())
# my_list = [1, 7, 14, 25, 18, 6, 12]

I kept the indices and values separate so you can also just pass lists or anything else, not just a mapping. Depending on your usecase you might want to pass the dictionary to the function instead.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.