3

I'm trying to make an example program in Python 2.7 which saves/shares states between two functions. You call a function, next time you call a function, it should remember the previous value. Here is my current code:

def stuff():
    global x 
    x = 100
    def f():
        global x
        x = x * 2
        return x
    def g():
        global x
        x = x * 4
        return x
    return (f, g)

a,b = stuff()
print(a());

This code works, BUT the catch is that x must not be considered as a global variable outside the scope of stuff()... (That is the whole point of embedding x within stuff() in the first place). So, would x be global, or is it local to stuff()?

4 Answers 4

4

In Python 3.x, you can use nonlocal statement:

def stuff():
    x = 100
    def f():
        nonlocal x
        x = x * 2
        return x
    def g():
        nonlocal x
        x = x * 4
        return x
    return f, g

>>> a, b = stuff()
>>> a()
200
>>> b()
800
>>> a()
1600
Sign up to request clarification or add additional context in comments.

2 Comments

In Python 2, you can get around the lack of nonlocal by using a mutable container to hold the x value. It's a bit ugly though.
I never got back to this, but you are correct. Using some like state = {x: 100} allows the code to be private to stuff().
3

If you need to support python 2.X, @georgek's answer is the best, but a lot of people don't realize that you can add attributes to functions. A common idiom is to use a list of a single element to hold the variable.

def stuff():
    x = [100]
    def g():
        x[0] = x[0]*2
        return x[0]
    def h():
        x[0] = x[0]*4
        return x[0]
    return (g,h)
a, b = stuff()
print(a())

This works because you never assign to x itself in the internal scopes, so it doesn't rebind the variable and shadow the closure.

Comments

3

Easiest and least hacky solution, use a class:

class stuff(object):
    x = 100
    def f(self):
        self.x = self.x * 2
        return self.x

    def g(self):
        self.x = self.x * 4
        return self.x

Result:

>>> s = stuff()
>>> s.f()
200
>>> s.g()
800

You want to prevent people from accessing x from outside the functions at all. The way you do that in Python is by prefixing it with an underscore:

class stuff(object):
    _x = 100
    def f(self):
        self._x = self._x * 2
        return self._x

    def g(self):
        self._x = self._x * 4
        return self._x

This tells other Python programmers that it is internal, and not to access it, except on their own risk. You seem to want to prevent even this, but you can't. You can even access the closure in the Python 3 nonlocal example as well:

def stuff():
    x = 100
    def f():
        nonlocal x
        x = x * 2
        return x

    return f

>>> a = stuff()
>>> a()
200
>>> a()
400
>>> a.__closure__[0].cell_contents
400

So you aren't preventing anyone from fiddling with it, you just make the fiddling it obscure and brittle. and more likely to fail. As such you just end up making things more difficult for everyone involved.

7 Comments

I'd say that being able to "only have one" is the biggest downside to the questioner's current code with global. Other than hiding the class, though, I think this is a great answer.
@Blckknght: Well, sometimes you want to have only one, when it is some sort of "pseudo-global" thing. I assume that's the case here since he tries to use a function closure.
Can't you call stuff.x tho and get the value of x? I'm trying to privatize x inside of a function, hence the def instead of use of a class...
@jadengore Yes, you can access it. This is not a problem. If you don't want to access it outside of the class, then just don't. You can it as internal by prefixing it with underscore: _x = 100, then other programmers will also know it's internal.
@LennartRegebro The whole point of using the def stuff() from the start is to prevent x from being accessed out of the scope of stuff(). It's not about telling other programmers its internal, I don't want anything outside the function to be able to access x unless it is being return. Want it to be private without using a module.
|
0

You can use a function without global:

>>> def f():
...     f.x = 100
...     def g():
...             f.x = f.x * 2
...             return f.x
...     def h():
...             f.x = f.x * 4
...             return f.x
...     return (g, h)
...
>>> a, b = f()
>>> a()
200
>>> b()
800
>>> a()
1600
>>>

2 Comments

So, does this make f.x private and exclusive to things only in the scope of f?
@jadengore No, f.x is still accessible.

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.