tags:

views:

569

answers:

11

In Perl 5.10, I can say:

sub foo () {
  state $x = 1;
  say $x++;
}

foo();
foo();
foo();

...and it will print out:

1
2
3

Does Python have something like this?

+2  A: 

Yes, though you have to declare your global variable first before it is encountered in foo:

x = 0

def foo():
    global x
    x += 1
    print x

foo()
foo()
foo()

EDIT: In response to the comment, it's true that python has no static variables scoped within a function. Note that x in this example is only exposed as global to the rest of the module. For example, say the code above is in test.py. Now suppose you write the following module:

from test import foo
x = 100
foo()
foo()

The output will be only 1 and 2, not 101 and 102.

Jarret Hardie
Not the same, because the variable is not scoped inside the function -- it's exposed to the rest of the code.
scrible
Yeah, you're right. Python does not have static variables, so this is the only way to do it without going the class route. However, note that this is only exposed to the rest of the module as global.
Jarret Hardie
state variables are lexical, not global (which is the main point of them, something LIKE a global that 'remembers' but without the downsides of being an actual global)
Adam Kennedy
+15  A: 

A class may be a better fit here (and is usually a better fit for anything involving "state"):

class Stateful(object):

    def __init__(self):
        self.state_var = 0

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

foo = Stateful()
foo()
foo()
Ali A
Why not: `self.x += 1`?
J.F. Sebastian
Just trying to be explicit in the example.
Ali A
+8  A: 

Python has generators which do something similar:

http://stackoverflow.com/questions/231767/can-somebody-explain-me-the-python-yield-statement

Justus
+2  A: 

You could also use something like

def static_num2():
    k = 0
    while True:
        k += 1
        yield k

static = static_num2().next

for i in range(0,10) :
    print static()

to avoid a global var. Lifted from this link about the same question.

Adriano Varoli Piazza
bad formatting in your code
Jiri
Also, in a fairly recent python, you could do `static= itertools.count().next` and drop the `static_num2` definition.
ΤΖΩΤΖΙΟΥ
+12  A: 

The closest parallel is probably to attach values to the function itself.

def foo():
    foo.bar = foo.bar + 1

foo.bar = 0

foo()
foo()
foo()

print foo.bar # prints 3
Triptych
+1 idiom for static variables in Python, although in practice you never need them.
bobince
you could return foo.bar
hasen j
+7  A: 

Not sure if this is what you're looking for, but python has generator functions that don't return a value per se, but a generator object that generates a new value everytime

def gen():
   x = 10
   while True:
      yield x
      x += 1

usage:

>>> a = gen()
>>> a.next()
10
>>> a.next()
11
>>> a.next()
12
>>> a.next()
13
>>>

look here for more explanation on yield:
http://stackoverflow.com/questions/231767/can-somebody-explain-me-the-python-yield-statement

hasen j
+1  A: 
>>> def foo():
    x = 1
    while True:
        yield x
        x += 1


>>> z = iter(foo())
>>> next(z)
1
>>> next(z)
2
>>> next(z)
3
Selinap
+1  A: 

Not that I'm recommending this, but just for fun:

def foo(var=[1]):
    print var[0]
    var[0] += 1

This works because of the way mutable default arguments work in Python.

dF
+5  A: 

Here's one way to implement a closure in python:

def outer():
    a = [4]
    def inner():
        print a[0]
        a[0] = a[0] + 1
    return inner

fn = outer()
fn() # => 4
fn() # => 5
fn() # => 6

I borrowed this example verbatim from a python mailing list post.

popcnt
+1  A: 

The preferable way is to use class or generator (yield).

For the sake of completeness here's a variant w/ closure in Python 3.x:

>>> def make_foo():
...     x = 1
...     def foo():
...         nonlocal x
...         print(x)
...         x += 1
...     return foo
...
>>> foo = make_foo()
>>> foo()
1
>>> foo()
2
>>> foo()
3
J.F. Sebastian
For all Python ≥2.5: import itertools; foo = itertools.count(1).next
ΤΖΩΤΖΙΟΥ
@ΤΖΩΤΖΙΟΥ: Wrong. NOTE: there is a `print`. Anyway the purpose is to demonstrate how to bind nonlocal name.
J.F. Sebastian
A: 

Here's another dirty cheap way to do it, it's a variation on Tryiptich's answer, but using decorators

def static_var( name, value ):
    def dec( function ):
        setattr( function, name, value )
        return function
    return dec


@static_var( 'counter', 0 )
def counting_function():
    counting_function.counter = counting_function.counter + 1
    print counting_function.counter



"""
>>> counting_function()
1
>>> counting_function()
2
>>> counting_function()
3
>>> counting_function()
4
>>> counting_function()
5
>>> 
"""
hasen j