470,632 Members | 2,454 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 470,632 developers. It's quick & easy.

Pythonic way to do static local variables?

I've a function that needs to maintain an ordered sequence between
calls.

In C or C++, I'd declare the pointer (or collection object) static at
the function scope.

What's the Pythonic way to do this?

Is there a better solution than putting the sequence at module scope?

Thanks.

Jul 19 '05 #1
11 4898
> I've a function that needs to maintain an ordered sequence between
calls.

In C or C++, I'd declare the pointer (or collection object) static at
the function scope.

What's the Pythonic way to do this?

Is there a better solution than putting the sequence at module scope?


Yes, there is; it's called "object oriented programming". In general,
whenever you find that one or more functions have to access a context
that is not passed in explicitly as arguments, the proper (pythonic and
non-pythonic) way is to define them as a method in a class that stores
this context.

class Foo(object):
def __init__(self):
self._context = # something

def method(self):
x = self._context
# do stuff
self._context.update()
Another solution is function/method attributes. This is handy if OO
seems an overkill for a single function, or in case of methods, if you
don't want to pollute the instance namespace with something used by a
single method:

def foo():
# foo.x is like a C static
try: foo.x +=1
except AttributeError:
foo.x = 1
return foo.x

for i in xrange(10): print foo()

if foo() is a method, it becomes a little more cumbersome, first
because you have to refer to the class name and (less obviously)
because user defined attributes are not allowed in instance methods (I
wonder why); you have to refer to the wrapped function:

class Foo(object):
def foo(self):
try: Foo.foo.im_func.x += 1
except AttributeError:
Foo.foo.im_func.x = 1
return Foo.foo.x

foo = Foo().foo
for i in xrange(10): print foo()
Hope this helps,

George

Jul 19 '05 #2
Well, if you're a c++ programmer, then you've probably ran into
`functors' at one time or another. You can emulate it by making a
python object that is `callable'.

class functor:
def __init__(self):
self.ordered_sequence = [1, 2, 3, 4, 5]
def __call__(self, arg1, arg2):
self.ordered_sequence.extend((arg1,arg2))
self.ordered_sequence.sort()
f = functor()
f(3,5)
f.ordered_sequence
[1, 2, 3, 3, 4, 5, 5]

Hope that helps some.
jw

On 4/25/05, Charles Krug <cd****@worldnet.att.net> wrote: I've a function that needs to maintain an ordered sequence between
calls.

In C or C++, I'd declare the pointer (or collection object) static at
the function scope.

What's the Pythonic way to do this?

Is there a better solution than putting the sequence at module scope?

Thanks.

--
http://mail.python.org/mailman/listinfo/python-list

Jul 19 '05 #3
Charles Krug wrote:
I've a function that needs to maintain an ordered sequence between
calls.

In C or C++, I'd declare the pointer (or collection object) static at
the function scope.

What's the Pythonic way to do this?

Is there a better solution than putting the sequence at module scope?

Thanks.


You might want a generator. Search for yield keyword.
Regards,
Nicolas
Jul 19 '05 #4
Charles Krug <cd****@worldnet.att.net> writes:
I've a function that needs to maintain an ordered sequence between
calls.

In C or C++, I'd declare the pointer (or collection object) static at
the function scope.

What's the Pythonic way to do this?

Is there a better solution than putting the sequence at module scope?


I'm not sure what you mean by "an ordered sequence". Assuming that a
static counter variable will do the trick (i.e. - it's values will
sequence across calls), you can do this in a number of ways:

1) Use a class:

class counterClass:
def __init__(self):
self.count = 1
def __call__(self):
self.count = self.count + 1

counterFunc = counterClass()

2) Use an instance variable of the function:

def counterFunc():
foo.counter = foo.counter + 1
foo.counter = 1

You ought to be able to do this with closures as well, but I couldn't
seem to get that to work.

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Jul 19 '05 #5
On Mon, 25 Apr 2005 21:30:18 -0500, Jaime Wyant
<pr***********@gmail.com> wrote:
Well, if you're a c++ programmer,
Well, my forte is embedded systems and device controls . . .
then you've probably ran into
`functors' at one time or another. You can emulate it by making a
python object that is `callable'.

class functor:
def __init__(self):
self.ordered_sequence = [1, 2, 3, 4, 5]
def __call__(self, arg1, arg2):
self.ordered_sequence.extend((arg1,arg2))
self.ordered_sequence.sort()


"ordered" in this case doesn't mean "sorted." . . .
8-)

It's the set of filter coefficients and cumulative remainders for an
overlap add convolution. Sorting would be . . . bad. Like crossing the
streams bad.

Both of these techniques look promising here.
Thanks
Jul 19 '05 #6

"Charles Krug" <cd****@worldnet.att.net> wrote in message
news:7U*********************@bgtnsc05-news.ops.worldnet.att.net...
Both of these techniques look promising here.


Here a third, the closure approach (obviously not directly tested):

def func_with_static(datalist):
def real_func(otherargs):
<code that uses datalist>
return real_func

func1 = func_with_static([.1, 99, 1e-10,...])

If needed, real_func can *modify* (but not *rebind*) datalist.
Unlike the attribute approach, but like the class approach, you can
simultaneously have have multiple closures with different static data

func2 = func_with_static([.2, 152, 1e-9, ...])

Terry J. Reedy

Jul 19 '05 #7
Terry Reedy wrote:
"Charles Krug" <cd****@worldnet.att.net> wrote in message
news:7U*********************@bgtnsc05-news.ops.worldnet.att.net...
Both of these techniques look promising here.

Here a third, the closure approach (obviously not directly tested):

Just for grins, here's a fourth approach, using the descriptor protocol
def func_with_state(state,a): ... state.append(a)
... return state
... f = func_with_state.__get__([])
f(1) [1] f(2) [1, 2] f(3) [1, 2, 3]


Michael

Jul 19 '05 #8
A quick, hackish way to keep a static variable is to declare it as a
parameter and give it a default value. The parameter list is evaluated
when the function is compiled, not when it is called. The underscores
are added as per convention to indicate that the variable is
special/private.

Example-

def cumulative_sum(arg, __static__ = []):
__static__.append(arg)
return reduce(lambda a,b: a + b, __static__)

#-------------------
cumulative_sum(1) 1 cumulative_sum(1) 2 cumulative_sum(1)

3

Jul 19 '05 #9
A quick, hackish way to keep a static variable is to declare it as a
parameter and give it a default value. The parameter list is evaluated
when the function is compiled, not when it is called. The underscores
are added as per convention to indicate that the variable is
special/private.

Example-

def cumulative_sum(arg, __static__ = []):
__static__.append(arg)
return reduce(lambda a,b: a + b, __static__)

#-------------------
cumulative_sum(1) 1 cumulative_sum(1) 2 cumulative_sum(1)

3

Jul 19 '05 #10
On Tue, 26 Apr 2005 10:26:51 -0700, Michael Spencer <ma**@telcopartners.com> wrote:
Terry Reedy wrote:
"Charles Krug" <cd****@worldnet.att.net> wrote in message
news:7U*********************@bgtnsc05-news.ops.worldnet.att.net...
Both of these techniques look promising here.

Here a third, the closure approach (obviously not directly tested):

Just for grins, here's a fourth approach, using the descriptor protocol
>>> def func_with_state(state,a): ... state.append(a)
... return state
... >>> f = func_with_state.__get__([])
>>> f(1) [1] >>> f(2) [1, 2] >>> f(3) [1, 2, 3] >>>

For those who don't recognize it, this is the mechanism that binds instances to
methods of their type to make bound methods. It's as if you had given list a
new method and picked up the bound method from an instance like [].func_with_state
def func_with_state(state, a): ... state.append(a)
... return state
... f = func_with_state.__get__([])
f <bound method ?.func_with_state of []>

The '?' is because we didn't supply the second argument to __get__, i.e., type([])
f = func_with_state.__get__([], list)
f <bound method list.func_with_state of []>
f.im_func <function func_with_state at 0x02EFD614> func_with_state <function func_with_state at 0x02EFD614>
For grins, here's a fifth approach (I'm assuming you counted correctly to the fourth ;-)
from ut.presets import presets
@presets(state=[]) ... def f(a):
... state.append(a)
... return state
... f(1) [1] f(2) [1, 2] f(3) [1, 2, 3]

This is a straight function, with byte-code hack preset of internal state as specified.
The disassemby shows [1, 2, 3] because that is the object referred to still. What would have
been a global reference got hacked to LOAD_FAST 1 (state) after preset from "constant".
import dis
dis.dis(f) 1 0 LOAD_CONST 1 ([1, 2, 3])
3 STORE_FAST 1 (state)

3 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP

4 19 LOAD_FAST 1 (state)
22 RETURN_VALUE

To see the initial state, we have to re-decorate another instance of f:
@presets(state=[]) ... def f(a):
... state.append(a)
... return state
... dis.dis(f) 1 0 LOAD_CONST 1 ([])
3 STORE_FAST 1 (state)

3 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP

Of course, we could put this inside a factory and pass the desired state
def factory(state): ... @presets(state=state)
... def f(a):
... state.append(a)
... return state
... return f
... f2 = factory([111,222])
f2('third') [111, 222, 'third'] dis.dis(f2) 2 0 LOAD_CONST 1 ([111, 222, 'third'])
3 STORE_FAST 1 (state)

4 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP

5 19 LOAD_FAST 1 (state)
22 RETURN_VALUE dis.dis(factory('append will fail')) 2 0 LOAD_CONST 1 ('append will fail')
3 STORE_FAST 1 (state)

4 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP

5 19 LOAD_FAST 1 (state)
22 RETURN_VALUE

Note that the function takes only one argument:
f2 <function f at 0x02EF841C> f2.func_code.co_argcount 1 f2(10, 'fails')

Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: f() takes exactly 1 argument (2 given)

Regards,
Bengt Richter
Jul 19 '05 #11
On 26 Apr 2005 13:58:23 -0700, "Lonnie Princehouse" <fi**************@gmail.com> wrote:
A quick, hackish way to keep a static variable is to declare it as a
parameter and give it a default value. The parameter list is evaluated
when the function is compiled, not when it is called. The underscores
are added as per convention to indicate that the variable is
special/private.

Example-

def cumulative_sum(arg, __static__ = []):
__static__.append(arg)
return reduce(lambda a,b: a + b, __static__)

#-------------------
cumulative_sum(1)1 cumulative_sum(1)2 cumulative_sum(1)

3

This default-value hack is what my presets decorator was motivated to replace
(if you are willing to depend to a byte-code-hacking decorator ;-)

Regards,
Bengt Richter
Jul 19 '05 #12

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

3 posts views Thread by IHateSuperman | last post: by
2 posts views Thread by Steve | last post: by
9 posts views Thread by Bryan Parkoff | last post: by
3 posts views Thread by Datta Patil | last post: by
2 posts views Thread by blue | last post: by
3 posts views Thread by David MacKay | last post: by
4 posts views Thread by Carl J. Van Arsdall | last post: by
16 posts views Thread by RB | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.