Search code examples
pythonvariadic-functionsgenerator-expressionargument-unpacking

Difference between a loop in a function call and listing all the arguments explicitly


I have a function that sorts a list of lists by the first list. When I use the function with the variables like so:

sort_lists(IN[0],IN[1],IN[2])

it works perfectly. Although, as I don't know how many lists my input contains, I want to use this as my variable:

sort_lists(IN[idx] for idx in range(len(IN)))

Although this returns a sorting of one list (the superlist). Why is there a difference between these variables, and how can I improve the code?

Here is the function if decisive (here IN[0] is the input with a number of sublists):

def sort_lists(*args):
    zipped_list= zip(*sorted(zip(*args)))
    return [list(l) for l in zipped_list]

OUT = sort_lists(data_sort[0],data_sort[1],data_sort[2])

I want to use this output:

OUT = sort_lists(data_sort[idx] for idx in range(len(IN[0])))

Solution

  • Two things to understand here:

    You can see how your inputs are different if you simply add print statement in your function:

    def sort_lists(*args):
        print(args)
        zipped_list= zip(*sorted(zip(*args)))
        return [list(l) for l in zipped_list]
    

    Let the input list of lists be: lists = [[2, 1, 3], [1, 3, 4], [5, 4, 2]].

    sort_lists(lists[0], lists[1], lists[2])
    

    will print: ([2, 1, 3], [1, 3, 4], [5, 4, 2]). That's a tuple of inner lists.

    Though, if you call it like this:

    sort_lists(lists[idx] for idx in range(len(lists)))
    

    or

    sort_lists(sublist for sublist in lists)
    

    this will print (<generator object <genexpr> at 0x0000007001D3FBA0>,), a one-element tuple of a generator.

    You can make your function work with a generator by accepting only one parameter:

    def sort_lists(arg):
        zipped_list= zip(*sorted(zip(*arg)))
        return [list(l) for l in zipped_list]
    
    sort_lists(lists[idx] for idx in range(len(lists)))
    # [[1, 2, 3], [3, 1, 4], [4, 5, 2]]
    

    but I suggest to leave your function as is, and unpack your lists in the place where you call it instead:

    >>> sort_lists(*lists)
    [[1, 2, 3], [3, 1, 4], [4, 5, 2]]