Persisting private function state between calls (was: "Read Only" namespaces)

On Sat, Mar 29, 2014 at 9:51 AM, Richard Prosser <richard.prosser@mail.com> wrote:
Quite simply, the principle is to allow changes to variables within a function body (for example) but not outside of it, rather like the Functional Programming paradigm. That of course excludes the use of global variables and requires functions to return whatever is needed to the outer scope. In my experience, globals are only needed to implement persistent (inter-call) states anyway, so assuming that such a feature is available in the language I believe that the "Read Only" proposal is viable.
To restate, you want a way to have some variables in your function body that persist between calls without using global variables. There are already several ways to do this, though none is particularly obvious. Let's use a simple example using a global variable followed by the alternative solutions. current = 0 def adder(x): global current current += x return current 1. Still use a global variable, but make it "private" (just use the convention of a leading underscore on the name): _current = 0 def adder(x): global _current _current += x return _current 2. Use the "default argument hack": def adder(x, _ns={'current': 0}): _ns['current'] += x return _ns['current'] or class IntWrapper: def __init__(self, value=0): self.value = value def adder(x, _current=IntWrapper()): _current.value += x return _current.value 3. Use a closure (making use of the "nonlocal" keyword): def get_adder(start=0): current = start # Done for clarity. def adder(x): nonlocal current current += x return current return adder adder = get_adder() Each of the four approaches has its pros and cons. The closure approach is probably the clearest if you really want to avoid using a global. If there were agreement on a best approach, the least we could do it make it clearer in the docs, assuming you looked and didn't find anything helpful in the obvious places. :) A more explicit mechanism could be nice, but would have to pass a pretty high bar for inclusion into the language. Here are some approaches: * A new keyword, e.g. "persistent", to accompany global and nonlocal. * A special decorator that identifies persistent local variables in the function. So that you're aware, this idea came up a few years ago and involved a long discussion that eventually lost steam (don't remember why) [1][2][3]. There are also a couple existing PEPs [4][5] that would be applicable (my favorite is the given statement). -eric [1] https://mail.python.org/pipermail/python-ideas/2011-September/011805.html [2] https://mail.python.org/pipermail/python-ideas/2011-June/010479.html [3] https://mail.python.org/pipermail/python-ideas/2011-September/011750.html [4] "statement local namespaces" (given statement) http://www.python.org/dev/peps/pep-3150/ [5] "General purpose decorator clause" http://www.python.org/dev/peps/pep-0403/

On Sat, Mar 29, 2014 at 12:39:31PM -0600, Eric Snow wrote:
On Sat, Mar 29, 2014 at 9:51 AM, Richard Prosser <richard.prosser@mail.com> wrote:
Quite simply, the principle is to allow changes to variables within a function body (for example) but not outside of it, rather like the Functional Programming paradigm. That of course excludes the use of global variables and requires functions to return whatever is needed to the outer scope. In my experience, globals are only needed to implement persistent (inter-call) states anyway, so assuming that such a feature is available in the language I believe that the "Read Only" proposal is viable.
To restate, you want a way to have some variables in your function body that persist between calls without using global variables.
Ah-ha! That makes sense now. Thank you!
There are already several ways to do this, though none is particularly obvious.
I disagree -- I think your example of an adder() function with a global is extremely obvious. It seems to be the usual solution that most beginners go for. It's not a *good* solution, but it is simple and obvious. A much better solution if you need persistent state is to encapsulate the function and state into an object: class Adder: def __init__(self): self.current = 0 def add(self, x): self.current += x return self.current Then you can have as many Adder instances as you want, without them all sharing state. adder1 = Adder().add adder2 = Adder().add [...]
A more explicit mechanism could be nice, but would have to pass a pretty high bar for inclusion into the language. Here are some approaches:
* A new keyword, e.g. "persistent", to accompany global and nonlocal. * A special decorator that identifies persistent local variables in the function.
-1 to both of those. Not every idiom needs a keyword. -- Steven

On Sat, Mar 29, 2014 at 12:58 PM, Steven D'Aprano <steve@pearwood.info> wrote:
On Sat, Mar 29, 2014 at 12:39:31PM -0600, Eric Snow wrote:
There are already several ways to do this, though none is particularly obvious.
I disagree -- I think your example of an adder() function with a global is extremely obvious. It seems to be the usual solution that most beginners go for. It's not a *good* solution, but it is simple and obvious.
Agreed. It was the example that mapped most closely to how the OP described his problem. :) [...]
A more explicit mechanism could be nice, but would have to pass a pretty high bar for inclusion into the language. Here are some approaches:
* A new keyword, e.g. "persistent", to accompany global and nonlocal. * A special decorator that identifies persistent local variables in the function.
-1 to both of those. Not every idiom needs a keyword.
Oh, I agree*. I should have made it clear. :) I would advocate instead for a more general solution such as that provided by statement local namespaces (e.g. PEP 3150/403). -eric * I do think it may be worth exploring the idea of special decorators that set code/function object flags that influence special-case behaviors. However, the performance implications of handling such flags at call-time *may* render the idea impractical.
participants (2)
-
Eric Snow
-
Steven D'Aprano