David Metcalfe
David Metcalfe

Reputation: 2431

Looping over *args resets counter

I'm working on some made up code to try to understand *args functionality and am having difficulty getting my counter variable to not reset.

After the 4th item, the numbering resets back to 0, but I want it to continue across the args.

lst = ['apple', 'banana', 'orange', 'lemon']
lst2 = ['apple2', 'banana2', 'orange2', 'lemon2']
lst3 = ['apple3', 'banana3', 'orange3', 'lemon3']


def generateMenu(*args):
    counter = 0

    for i in args:
        def recurse(l, counter):
            for i in l:
                counter += 1
                if isinstance(i, (list, tuple)):
                    recurse(i, counter)
                else:
                    print("{}. {}.".format(counter, i))
        recurse(i, counter)


generateMenu(lst, lst2, lst3)

Output:

1. apple.
2. banana.
3. orange.
4. lemon.
1. apple2.
2. banana2.
3. orange2.
4. lemon2.
1. apple3.
2. banana3.
3. orange3.
4. lemon3.

Upvotes: 1

Views: 58

Answers (3)

Ryan Collingham
Ryan Collingham

Reputation: 99

Python has slightly odd rules about writing to variables outside of the function's scope. The easiest way to avoid this IMO is just to make sure that your recursive function returns the counter value at each state. I've slightly refactored to make it a bit clearer what is going on.

lst = [('apple', 'banana'), 'orange', 'lemon']
lst2 = ['apple2', 'banana2', 'orange2', 'lemon2']
lst3 = ['apple3', 'banana3', 'orange3', 'lemon3']


def generateMenu(*args):
    outer_counter = 1

    def recurse(l, inner_counter):
        for i in l:
            if isinstance(i, (list, tuple)):
                inner_counter = recurse(i, inner_counter)
            else:
                print("{}. {}.".format(inner_counter, i))
                inner_counter += 1
        return inner_counter

    for i in args:
        outer_counter = recurse(i, outer_counter)


generateMenu(lst, lst2, lst3)

Upvotes: 1

John La Rooy
John La Rooy

Reputation: 304473

You should make counter a nonlocal (nonlocal was introduced in Python3)

lst = ['apple', 'banana', 'orange', 'lemon']
lst2 = ['apple2', 'banana2', 'orange2', 'lemon2']
lst3 = ['apple3', 'banana3', 'orange3', 'lemon3']


def generateMenu(*args):
    counter = 0

    for i in args:
        def recurse(l):
            nonlocal counter
            for i in l:
                counter += 1
                if isinstance(i, (list, tuple)):
                    recurse(i)
                else:
                    print("{}. {}.".format(counter, i))
        recurse(i)


generateMenu(lst, lst2, lst3)

Alternatively, return counter from recurse

lst = ['apple', 'banana', 'orange', 'lemon']
lst2 = ['apple2', 'banana2', 'orange2', 'lemon2']
lst3 = ['apple3', 'banana3', 'orange3', 'lemon3']

def generateMenu(*args):
    counter = 0

    for i in args:
        def recurse(l, counter):
            for i in l:
                counter += 1
                if isinstance(i, (list, tuple)):
                    counter = recurse(i, counter)
                else:
                    print("{}. {}.".format(counter, i))
            return counter
        counter = recurse(i, counter)


generateMenu(lst, lst2, lst3)

Upvotes: 2

errata
errata

Reputation: 6041

The problem is that your recurse function is not aware of your counter from upper scope. So what you are increasing in each for i in l: loop is just the "inner" counter variable.

Your example might confuse you because you are using same variable names twice (counter and i). Try to rename them in a different way and you might get a better idea of what is happening with your code.

Upvotes: 1

Related Questions