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()?

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
jadengore
  • 453
  • 1
  • 5
  • 14

4 Answers4

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
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • 1
    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. – Blckknght Oct 10 '13 at 05:11
  • I never got back to this, but you are correct. Using some like `state = {x: 100}` allows the code to be private to `stuff()`. – jadengore Dec 04 '13 at 10:36
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.

Evan
  • 2,217
  • 15
  • 18
2

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.

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
  • 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 Oct 10 '13 at 05:14
  • @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. – Lennart Regebro Oct 10 '13 at 05:16
  • 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 Oct 10 '13 at 05:27
  • @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. – Lennart Regebro Oct 10 '13 at 05:42
  • @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. – jadengore Oct 10 '13 at 05:48
  • @jadengore: It's not private in a module either. The point is that nothing is private in Python, and that is intentional. You are just creating problems for yourself. – Lennart Regebro Oct 10 '13 at 05:58
  • @jadengore See http://stackoverflow.com/questions/1641219/does-python-have-private-variables-in-classes http://bytes.com/topic/python/answers/802792-why-does-python-not-have-mechanism-data-hiding etc – Lennart Regebro Oct 10 '13 at 06:00
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
>>>
georgek
  • 877
  • 6
  • 11