9

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?

mike
  • 46,876
  • 44
  • 102
  • 112
  • 2
    To python fans: Note that state variables are not just used for generators in Perl. They are a handy way of encapsulating information that needs to persist with some function throughout its lifetime. :) – Robert P Jul 21 '11 at 18:49

11 Answers11

18

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 Afshar
  • 40,967
  • 12
  • 95
  • 109
12

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
Kenan Banks
  • 207,056
  • 34
  • 155
  • 173
  • +1 idiom for static variables in Python, although in practice you never need them. – bobince Mar 03 '09 at 00:51
  • 2
    Didn’t you just create a non-private variable!? Oh my! – tchrist Nov 13 '10 at 17:49
  • 1
    I love how python fans generally refer to any language feature not directly included as 'not needed in practice.', regardless of feature. :) – Robert P Jul 21 '11 at 18:44
  • There is a lot in ANY language that is "not needed". But if it helps you write correct programs, it's a GOOD THING. Perl's state variables do that. – John Deighan Aug 16 '19 at 03:21
9

Python has generators which do something similar:

What does the "yield" keyword do in Python?

Community
  • 1
  • 1
Justus
  • 203
  • 1
  • 4
9

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:
What does the "yield" keyword do in Python?

Community
  • 1
  • 1
hasen
  • 161,647
  • 65
  • 194
  • 231
5

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
  • 4,519
  • 1
  • 16
  • 14
3

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.
  • 74,139
  • 30
  • 130
  • 136
2

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
  • 95,172
  • 10
  • 132
  • 126
  • 2
    Not the same, because the variable is not scoped inside the function -- it's exposed to the rest of the code. – Arkady Mar 03 '09 at 00:25
  • 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 Mar 03 '09 at 00:39
  • 1
    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 Oct 13 '09 at 01:38
  • 1
    Isn’t this sort of lack of support for proper scoping one of Python’s Achilles’ heels? Will this be fixed in Python 3000? – tchrist Nov 13 '10 at 17:51
2

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
  • 7,297
  • 5
  • 39
  • 50
  • Also, in a fairly recent python, you could do `static= itertools.count().next` and drop the `static_num2` definition. – tzot Mar 03 '09 at 22:49
2

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
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • For all Python ≥2.5: import itertools; foo = itertools.count(1).next – tzot Mar 03 '09 at 22:50
  • @ΤΖΩΤΖΙΟΥ: Wrong. NOTE: there is a `print`. Anyway the purpose is to demonstrate how to bind nonlocal name. – jfs Mar 03 '09 at 23:08
1
>>> def foo():
    x = 1
    while True:
        yield x
        x += 1


>>> z = iter(foo())
>>> next(z)
1
>>> next(z)
2
>>> next(z)
3
riza
  • 16,274
  • 7
  • 29
  • 29
1

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
  • 161,647
  • 65
  • 194
  • 231