tags:

views:

2223

answers:

6

What is the idiomatic Python equivalent of this C/C++ code?

void foo()
{
    static int counter = 0;
    counter++;
    printf("counter is %d\n", counter);
}

specifically, how does one implement the static member at the function level, as opposed to the class level? And does placing the function into a class change anything?

A: 

Use a generator function to generate an iterator.

def foo_gen():
    n = 0
    while True:
        n+=1
        yield n

Then use it like

foo = foo_gen().next
for i in range(0,10):
    print foo()

If you want an upper limit:

def foo_gen(limit=100000):
    n = 0
    while n < limit:
       n+=1
       yield n

If the iterator terminates (like the example above), you can also loop over it directly, like

for i in foo_gen(20):
    print i

Of course, in these simple cases it's better to use xrange :)

Here is the documentation on the yield statement.

gnud
That doesn't look like Python...
Greg Hewgill
Questioner asked for Python, not PHP
eglaser
I asked for Python, not PHP, but thanks anyway…
andrewdotnich
Ehm. Sorry, i thought I was reading questions tagged php. Other tab. Fixed it.
gnud
hmm.. a strange way of doing it, i'll post my version
Claudiu
This approach looks pretty unwieldy.
ojrac
Yes, i guess it's because it's unweildy that the yield statement exists. Because python is known for making things complicated. I assume that the asker doesnt really want to implement a counter, since that's better done with a simple variable. So I show how to create a simple iterator generator.
gnud
Not sure about the downvotes, but using a generator to store state seems the best immediate way.
Ali A
@Ali: the downvotes were because @gnud's initial solution was PHP, not Python :D
andrewdotnich
+13  A: 

A bit reversed, but this should work:

def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter
foo.counter = 0

You can create a decorator:

def static_var(varname, value):
    def decorate(func):
        setattr(func, varname, value)
        return func
    return decorate

Then use the code like this:

@static_var("counter", 0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

It'll still require you to use the 'foo.' prefix, unfortunately.

Claudiu
Not really static (in the C++ sense) if you change counter does it affect all other instances of foo()?
Martin Beckett
Yes, try it in the interpreter, his code works as expected.
Jeremy Banks
there is only one instance of foo - this one function. all invocations access the same variable.
Claudiu
That's cool -- never knew you could add attributes to a function!
mipadi
+3  A: 

In addition to the way Claudiu demonstrated, you can also do it like this. (Though it's probably a bad idea; it's not very clear.)

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 

Default values are initialized only when the function is first evaluated, not each time it is executed, so you can use a list or any other mutable object to maintain static values.

Jeremy Banks
I tried that, but for some reason, the function parameter was initialising itself to 140, not 0. Why would this be?
andrewdotnich
This doesn't look terribly Pythonic too me, but points for creativity.
bouvard
@andrewdotnich: I'm not sure why it would do that, I've tried it on 2.5, 2.6 and 3.0rc1, and it worked properly in each case. =\
Jeremy Banks
@bouvard: Yeah, I try to avoid using it in general, but for quick scripts whose code quality I'm not greatly concerned about, it can be convenient.
Jeremy Banks
@Jeremy: would the fact that the function is an instance method change anything?
andrewdotnich
@andrewdotnich: It seems to work for me – http://gist.github.com/23700
Jeremy Banks
@Jeremy: worked out the oddity, many thanks!
andrewdotnich
A: 
_counter = 0
def foo():
   global _counter
   _counter += 1
   print 'counter is', _counter

Python customarily uses underscores to indicate private variables. The only reason in C to declare the static variable inside the function is to hide it outside the function, which is not really idiomatic Python.

Dave
+3  A: 

You can add attributes to a function, and use it as a static variable.

def myfunc():
  myfunc.counter += 1
  print myfunc.counter

# attribute must be initialized
myfunc.counter = 0

Alternatively, if you don't want to setup the variable outside the function, you have to resort to getattr to avoid an AttributeNotFound exception, which is kind of ugly :

def myfunc():
  myfunc.counter += getattr(myfunc,"counter",0) # 0 is the default value

Anyway static variables are rather rare, and you should find a better place for this variable, most likely inside a class.

vincent
+7  A: 

Python doesn't have static variables but you can fake it by defineing a callable object and then use it as a function.

class Foo(object):
  def __init__(self):
    self.counter = 0

  def __call__(self):
    self.counter += 1
    print self.counter

foo = Foo()

foo() #prints 1
foo() #prints 2
foo() #prints 3
daniels
Not totally "static", because you could have multiple instances of the callable object. You can move counter into class definition to make it truly static.
S.Lott