MetalicSt33l
MetalicSt33l

Reputation: 45

Designate a variable as a list without overwriting it in a function

I created a function that will flatten a nested list into an ordinary list.

outputarray = []

def flattenarray(x):
    for elmt in x:
        if isinstance(elmt, float) or isinstance(elmt, int):
            outputarray.append(elmt)
        elif isinstance(elmt, list):
            flattenarray(elmt)
    return outputarray

The above works perfectly, but I am trying to have the "outputarray" variable inside the function, but when I do, the recursion step will overwrite the outputarray list back into an empty list.

How can I make this work to designate a list inside the function and at the same time be able to append to it without overwriting it during recursion?

Upvotes: 4

Views: 896

Answers (3)

wim
wim

Reputation: 363123

You will want to create the output array from within the function. One way to proceed is to pass the output container along in the recursive step:

def flattenarray(x, outputarray=None):
    if outputarray is None:
        outputarray = []
    for elmt in x:
        if isinstance(elmt, float) or isinstance(elmt, int):
            outputarray.append(elmt)
        elif isinstance(elmt, list):
            flattenarray(elmt, outputarray=outputarray)
    return outputarray

A more Pythonic approach is for the flattener to yield the items one-by-one. Note that isinstance can accept a tuple of types, so you only need to call it once.

def flatten(x):
    for elmt in x:
        if isinstance(elmt, (int, float, str, bytes)):
            yield elmt
        else:
            yield from flatten(elmt)

A more correctly duck-typing implementation:

def flatten(x):
    try:
        it = iter(x)
    except TypeError:
        yield x
        return
    if isinstance(x, (str, bytes)):
        yield x
        return
    for elem in it:
        yield from flatten(elem)

Upvotes: 6

Marcus.Aurelianus
Marcus.Aurelianus

Reputation: 1518

This works perfectly with one line function and the basic idea is the same with wim:

def flatten(lst, outputarray=[]):
    return sum( ([x] if not isinstance(x, (list,tuple))  else flatten(x)
                     for x in lst), outputarray)


lst=[1,2,3,[2,4,5,[6]],(7,8,9)]

print(flatten(lst))

Result:

[1, 2, 3, 2, 4, 5, 6, 7, 8, 9]

Upvotes: 2

Mad Physicist
Mad Physicist

Reputation: 114440

You could use the return value of your recursive function calls to extend the output instead of trying to insert into a global variable:

def flattenarray(x):
    outputarray = []
    for elmt in x:
        if isinstance(elmt, float) or isinstance(elmt, int):
            outputarray.append(elmt)
        elif isinstance(elmt, list):
            outputarray.extend(flattenarray(elmt))
    return outputarray

This creates many more temporary lists than strictly necessary, so isn't very efficient. On the other hand, it's just as easy to read as your original code.

On a different note, I'd like to suggest a generalization to your code. Instead of checking for three specific types and skipping everything else, you can just check if an element is iterable or not:

global variable:

def flattenarray(x):
    outputarray = []
    for elmt in x:
        try:
            outputarray.extend(flattenarray(elmt))
        except TypeError:
            # the error will be raised by the for loop in the recursive call
            outputarray.append(elmt)
    return outputarray

Upvotes: 0

Related Questions