1

So, I have a whole bunch of things doing basically the same thing with only a slight change in input. Perfect candidate to be function'd out and called each time with different parameters, right?

Apparently, python thinks I'm wrong.

So, here's a boiled-down version of my problem

def main():
    x = 10

    def helper(n):
      if (x > n):
          x -= n

There's dozens of other lines in the helper function, and it's called dozens of times in main, but these are the lines that are causing me the problem. I know that the helper function is using local scope instead of using the one from main, but my question is, why? and how do I fix it?

I don't want to make x global, I just want the helper to use the already existing x instead of making up its own. Without this helper function, the total number of lines of code is going to quadruple, at least, and the code will be a convoluted, unreadable mess.

EDIT: Also, Main uses x a whole bunch, so I can't just define it locally, in case that wasn't clear.

3
  • Are you getting an exception? UnboundLocalError perhaps? Commented Jan 27, 2015 at 21:06
  • You can simply send x as a parameter to helper()... Commented Jan 27, 2015 at 21:10
  • @alfasin -- Possibly. It really depends on what OP is expecting to happen with x (e.g. expecting x to be mutated inside the function but passing in an immutable object...) Commented Jan 27, 2015 at 21:15

2 Answers 2

4

In Python 3 you can do this by putting nonlocal x in the inner function. In Python 2 you can't do it.

However, what you can do instead is return the value from the helper function, and assign it where you call the helper function. It's a little hard to tell from your example, but I'm assuming you're using it something like this:

def main():
    x = 10

    def helper(n):
        if (x > n):
            x -= n

    # do something that changes x
    helper(2)
    # now x has changed

Instead, do this:

def helper(x, n):
    if (x > n):
        return x - n
    else:
        return x

def main():
    x = 10

    # do something that changes x
    x = helper(x, 2)
    # now x has changed

The idea is to pass the helper function the values it needs, and have it return the resulting value, which you can then assign (or do what you like with) at the place where the helper is called. This also often makes the code easier to follow, as opposed to having the helper function directly modify variables in its caller. Also notice that here you can define helper outside main, creating a cleaner separation between the two, and also making helper available for use in other functions.

Sign up to request clarification or add additional context in comments.

4 Comments

That's a good idea, but what if the case is that helper is changing more than one variable? I might consider migrating my entire project to Python 3 just to use nonlocal...
@MatthewFournier The helper can return a tuple object containing multiple values, and in the main context you can assign the local objects to their new values with the returned tuple.
@MatthewFournier: Then you should have it return all the values it wants to affect. However, the broader answer is "don't do that". Having functions that modify lots of external state instead of returning values makes for code that is difficult to understand, maintain, and debug. I suspect your actual function is rather baroque in structure and could be rewritten to avoid this sort of state-munging, but it's hard to know without knowing more about what you're actually doing.
All right, I've reworked my functions into only mutating one value. I'm marking this one as the answer then.
1

The problem is on the line: x -= n. Since you are doing an augmented assignment, python thinks (incorrectly) that x is a local variable. Unfortunately, x is a "nonlocal" variable. On python3.x, if you are OK with modifying x in the scope of main, then you can use the nonlocal keyword. However, I'm guessing you don't want to modify x in main's scope. A cross-version trick is to get yourself a local variable with the same value as the non-local version:

def main():
    x = 10
    def helper(n):
        lx = x
        if x > n:
           lx -= n
        # etc., etc.

    helper(12)  # ...

If you really want to mutate x in the scope of main, you might want to consider a class (with helper a method on the class):

class Main(object):  # Main for lack of a better name...
    def __init__(self, x):
        self.x = x
    def helper(self, n):
        if self.x > n:
            self.x -= n

def main():
    x = Main(10)
    ...
    x.helper(12)
    ...

6 Comments

It's hard to tell from the example in the question, but I don't think this will do what the OP wants, because I think the OP is trying to use the helper to actually modify the x in the outer function. Creating a new local inside helper will let you modify that new variable, but it won't "help" because it won't affect the x in main.
@BrenBarn -- Yeah, it is hard to tell. I've updated with another alternative.
This almost works. It's not giving an error and it's running properly, but it's not passing the lx value back to main afterwards.
@MatthewFournier -- Ahh, so you do want to mutate the x in main. In that case you need to return it from helper and re-bind it e.g. x = helper(n) or use the class based approach (which might be more trouble than it is worth).
@mgilson -- Problem is, there's helpers that modify more than one variable.
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.