805

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?

zerocukor287
  • 555
  • 2
  • 8
  • 23
andrewdotnich
  • 16,195
  • 7
  • 38
  • 57
  • 33
    There is **NO** equivalence I am afraid. Even if you do the decorator hack with function attributes, you will be able to access the variable outside, which kinda defeats the point, sadly. Moreover, you will have to hard code the function name in the function, which is very annoying. I would suggest to use a class or module global variables instead with the conventional `_` prefix. – László Papp Sep 03 '14 at 10:07
  • 17
    For non-C-programmers, [https://stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c#5033656](a static variable inside a function is only visible inside that function's scope, but its lifetime is the entire life of the program, and it's only initialized once). Basically, a persistent counter or storage variable that lives between function calls. – smci Jun 01 '18 at 08:28
  • 2
    @lpapp: there kind-of is, it's a [class member](https://stackoverflow.com/questions/12409714/python-class-members). You are correct that we can't prevent other code viewing it or changing it. – smci Jun 01 '18 at 08:37
  • I found [answer](https://stackoverflow.com/a/279586/15294463) given by Claudiu useful. – Vinit Ramesh Gore Nov 02 '21 at 20:26

30 Answers30

848

A bit reversed, but this should work:

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

If you want the counter initialization code at the top instead of the bottom, you can create a decorator:

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

Then use the code like this:

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

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

(Credit: @ony)

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • Not really static (in the C++ sense) if you change counter does it affect all other instances of foo()? – Martin Beckett Nov 10 '08 at 23:47
  • 31
    there is only one instance of foo - this one function. all invocations access the same variable. – Claudiu Nov 10 '08 at 23:49
  • 154
    Sorry for digging this up, but I'd rather put `if "counter" not in foo.__dict__: foo.counter = 0` as the first lines of `foo()`. This would help to avoid code outside the function. Not sure if this was possible back in 2008 though. P.S. Found this answer while searching for possibility to create static function variables, so this thread is still "alive" :) – binaryLV Aug 09 '12 at 06:30
  • 12
    @binaryLV: I'd probably prefer that to the first approach. The problem with the first approach is it isn't immediately obvious that `foo` and `foo.counter = ` are intimately related. however, I ultimately prefer the decorator approach, as there's no way the decorator will not be called and it is semantically more obvious what it does (`@static_var("counter", 0)` is easier on & makes more sense to my eyes than `if "counter" not in foo.__dict__: foo.counter = 0`, especially as in the latter you have to use the function name (twice) which might change). – Claudiu Mar 01 '13 at 19:31
  • 6
    @lpapp: It depends on what the point of static variables is. I always thought it was that it would be the same value across multiple function calls, which this does satisfy. I never took it to be about variable hiding, which this doesn't, as you said. – Claudiu Sep 03 '14 at 14:25
  • 12
    `def foo():` `if not hasattr(foo,"counter"): foo.counter=0` `foo.counter += 1` – Erik Aronesty Jan 03 '17 at 17:41
  • 2
    it should also be said: don't do this – whenitrains Jan 10 '20 at 02:29
  • IMO this is not really an answer to the question, but rather a quick fix (and an extremely ugly one at that). The accepted answer should have been "You can't, because Python simply does not have static variables"! – Vinzent May 28 '20 at 13:24
  • 1
    How if then we rename the function? for example `new_name = foo; del foo`. It wouldn't work, would it? Is there any better approach? I'm sorry I'm asking this just for educational purposes, and I don't have any specific purpose. – Hzzkygcs Jan 04 '21 at 17:58
  • 2
    @binaryLV Doesn't your suggestion create an additional overhead each time the function is called, since the interpreter now has to process an extra `if`-clause each time the function is called? – HelloGoodbye Dec 09 '22 at 14:27
284

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 can use hasattr() to avoid an AttributeError exception:

def myfunc():
  if not hasattr(myfunc, "counter"):
     myfunc.counter = 0  # it doesn't exist yet, so initialize it
  myfunc.counter += 1

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

sarnold
  • 102,305
  • 22
  • 181
  • 238
vincent
  • 6,368
  • 3
  • 25
  • 23
  • 9
    Why not try instead of if statement? – rav Dec 01 '13 at 15:01
  • 15
    `try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1` should do the same, using exceptions instead. – sleblanc Dec 10 '13 at 04:31
  • 4
    Exceptions ought to be used for Exceptional situations, i.e. ones the programmer expects will not happen, such as an input file it had successfully opened suddenly not being available. This is an expected situation, an if statement makes more sense. – Hack Saw Jan 15 '14 at 07:51
  • 16
    @Hack_Saw: Well, this is Pythonic (better to ask for forgiveness than permission). This is actually recommended in Python optimization techniques since it saves the cost of an if (though I'm not recommending premature optimization). Your rule about exceptional cases: 1. Failure IS an exceptional case here, in a sense. It only happens once. 2. I think that rule is about using (i.e. raising) exceptions. This is catching an exception for something you expect to work but have a backup plan for, which is a common thing in most languages. – leewz Jan 17 '14 at 23:42
  • 2
    @leewangzhong: Does enclosing a block that doesn't raise an exception within `try` add any cost? Just curious. – trss Jul 23 '14 at 20:04
  • trss: on a very dumb counter++ loop it has a 5% cost. But anyway, the loop is 2x faster if the counter is a global variable instead of an attribute. If it matters, such part belongs to a compiled library instead of pure python. – vincent Jul 23 '14 at 20:22
  • @vincent : what you have posted in your comment is correct but not from decorators perspective but from static variable explanation. The example that you have shown would be wrong from decorators perspective cause the inside (nested) function never gets called until the end of code where the function reference makes an explicit call to the inside function passed from decorator. If the inside function uses hasattr(func,"counter") , the decorator will fail big time cause the inside function is never reached and myfunc.counter never gets initialized to zero causing an AttributeError. – skyrocker Feb 07 '17 at 04:43
261

One could also consider:

def foo():
    try:
        foo.counter += 1
    except AttributeError:
        foo.counter = 1

Reasoning:

  • much pythonic ("ask for forgiveness not permission")
  • use exception (thrown only once) instead of if branch (think StopIteration exception)
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
rav
  • 3,579
  • 1
  • 18
  • 18
  • 12
    I haven't been doing Python long, but this satisfies one of the implicit tenements of the language: **if it's not (fairly) easy, you're doing it wrong**. – ZX9 Sep 21 '16 at 17:19
  • 2
    Did not work immediately with class methods, "self.foo.counter = 1" raises AttributeError again. – villasv Oct 14 '16 at 21:10
  • 20
    This is the correct solution and it should be the accepted answer because the initialisation code will be run when the function is called and not when the module is executed or when something from it is imported, which is the case if you use the decorator approach from the currently accepted answer. See [Python decorator function execution](http://stackoverflow.com/questions/36435236/python-decorator-function-execution). If you have a huge library module then every decorator will be run, including those of functions you do not import. – Nils Lindemann May 08 '17 at 09:48
  • 6
    A simpler approach : `def fn(): if not hasattr(fn, 'c'): fn.c = 0` `fn.c += 1 return fn.c` – MANU Dec 18 '17 at 05:12
  • 10
    @MANU Using `hasattr()` for this is not simpler and also less efficient. – moooeeeep Feb 08 '19 at 11:39
  • @moooeeeep what makes using `hasattr` slower than a try/except block? – qwerty_url Dec 22 '22 at 17:35
  • @qwerty_url Because of function call overhead, I guess. But it seems to skip some effort with the exception handling. So, if the negative case is common (which it here isn't), it actually becomes preferable to check before access (related: https://stackoverflow.com/a/903238/1025391). – moooeeeep Jan 09 '23 at 08:44
77

Many people have already suggested testing 'hasattr', but there's a simpler answer:

def func():
    func.counter = getattr(func, 'counter', 0) + 1

No try/except, no testing hasattr, just getattr with a default.

Jonathan
  • 1,864
  • 17
  • 26
  • 5
    pay attention to the third parm of getattr when you put a func there for example: def func(): def foo(): return 1112 func.counter = getattr(func, 'counter', foo()) + 1 when you call func,the foo will always be called! – Codefor Nov 19 '15 at 10:36
  • 4
    Just a call to getattr every time that func gets called. That's fine if performance isn't an issue, if it is try/except will win hands down. – Mark Lawrence Feb 18 '18 at 00:20
  • 7
    @MarkLawrence: Actually, at least on my Windows x64 3.8.0 install, the performance difference between this answer and [ravwojdyla's equivalent `try`/`except` based approach](https://stackoverflow.com/a/16214510/364696) is pretty meaningless. A simple `ipython` `%%timeit` microbenchmark gave the cost of the `try`/`except` at 255 ns per call, vs. 263 ns for the `getattr` based solution. Yes, the `try`/`except` is faster, but it's not exactly "winning hands down"; it's a tiny micro-optimization. Write whatever code seems clearer, don't worry about trivial performance differences like this. – ShadowRanger Dec 28 '19 at 02:47
  • 2
    @ShadowRanger thanks for benchmarking that. I've been wondering about MarkLawrence's statement for 2 years, and I'm very happy you did the research. I definitely agree with your final sentence - "write whatever code seems clearer" - that's exactly why I wrote this answer. – Jonathan Feb 06 '20 at 03:05
60

Other answers have demonstrated the way you should do this. Here's a way you shouldn't:

>>> 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 store static values.

Jeremy
  • 1
  • 85
  • 340
  • 366
  • I tried that, but for some reason, the function parameter was initialising itself to 140, not 0. Why would this be? – andrewdotnich Nov 10 '08 at 23:58
  • @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 Nov 11 '08 at 00:06
  • @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 Nov 11 '08 at 00:07
  • @Jeremy: would the fact that the function is an instance method change anything? – andrewdotnich Nov 11 '08 at 00:09
  • @JeremyBanksʬʬʬ: Isn't that the more efficient than `foo.counter += 1`? I have read it somewhere, that for performance the dots should be avoided. Maybe it is at least a little more efficient than `foo.counter`? Having performance in mind, would you choose `foo.counter` or `def foo(counter=[0])`? – Tadeck Feb 02 '12 at 06:13
  • @Tadeck I suspect that the dot-lookup would be a little bit faster (because it has to make the equivalent of multiple dot-lookups under-the-hood), but the difference should be insignificant in almost all cases. (I guess that means I'd pick `foo.counter`, because it's better style and possibly better performance.) – Jeremy Feb 02 '12 at 23:56
  • 1
    @bouvard For recursive functions that need a static variable, this is the only one that really reads well. – lifebalance Jun 05 '17 at 16:57
  • 1
    I tried several approaches and I wish this one becomes accepted as pythonic. With some meaningfull names like `def foo(arg1, arg2, _localstorage=DataClass(counter=0))` I find it well readable. Another good point is easy function renaming. – VPfB Jan 30 '18 at 12:40
  • 2
    Why do you say you shouldn't do it that way? Looks perfectly reasonable to me! – Konstantin Jul 29 '18 at 08:01
  • 2
    @VPfB: For general storage, you could use [`types.SimpleNamespace`](https://docs.python.org/3/library/types.html#types.SimpleNamespace), making it `def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):` without needing to define a special class. – ShadowRanger Dec 28 '19 at 02:25
  • My variable is already mutable (a set)! So this is just the solution I need. Somehow forgot about that nice option – Tomerikoo Feb 14 '21 at 15:02
33

Python doesn't have static variables but you can fake it by defining a callable class object and then using it as a function. Also see this answer.

class Foo(object):
  # Class variable, shared by all instances of this class
  counter = 0

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

# Create an object instance of class "Foo," called "foo"
foo = Foo()

# Make calls to the "__call__" method, via the object's name itself
foo() #prints 1
foo() #prints 2
foo() #prints 3

Note that __call__ makes an instance of a class (object) callable by its own name. That's why calling foo() above calls the class' __call__ method. From the documentation:

Instances of arbitrary classes can be made callable by defining a __call__() method in their class.

Community
  • 1
  • 1
daniels
  • 18,416
  • 31
  • 103
  • 173
  • 18
    Functions are already objects so this just adds an unnecessary layer. – DasIch Jan 30 '11 at 14:48
  • See this SO answer for a long opinion that this is actually a good idea. http://stackoverflow.com/questions/460586/. I agree that making any such class a singleton, perhaps like this http://stackoverflow.com/questions/6760685/, would also be a good idea. I don't know what @S.Lott means by "... move counter into class definition ..." because it looks like it's already in class-variable position to me. – Reb.Cabin Oct 20 '15 at 17:05
  • 1
    Based on my research, this class technique appears to be the most "Pythonic" of the approaches presented on this page, and uses the least trickery. I therefore plan on adopting it as my go-to replacement for C-static-like variables in functions, as a new Python developer myself. – Gabriel Staples Aug 14 '16 at 22:55
  • 1
    What happens if I want foo1 = Foo() and foo2 = Foo()? – Mark Lawrence Feb 18 '18 at 00:22
  • @MarkLawrence Then you have two different instances of a callable class, each with their own counter. Which exactly what you should expect if you aren't using the instance `foo` which is provided as a singleton. – Aaron McMillin Nov 27 '18 at 15:11
32

Here is a fully encapsulated version that doesn't require an external initialization call:

def fn():
    fn.counter=vars(fn).setdefault('counter',-1)
    fn.counter+=1
    print (fn.counter)

In Python, functions are objects and we can simply add, or monkey patch, member variables to them via the special attribute __dict__. The built-in vars() returns the special attribute __dict__.

EDIT: Note, unlike the alternative try:except AttributeError answer, with this approach the variable will always be ready for the code logic following initialization. I think the try:except AttributeError alternative to the following will be less DRY and/or have awkward flow:

def Fibonacci(n):
   if n<2: return n
   Fibonacci.memo=vars(Fibonacci).setdefault('memo',{}) # use static variable to hold a results cache
   return Fibonacci.memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2)) # lookup result in cache, if not available then calculate and store it

EDIT2: I only recommend the above approach when the function will be called from multiple locations. If instead the function is only called in one place, it's better to use nonlocal:

def TheOnlyPlaceStaticFunctionIsCalled():
    memo={}
    def Fibonacci(n):
       nonlocal memo  # required in Python3. Python2 can see memo
       if n<2: return n
       return memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2))
    ...
    print (Fibonacci(200))
    ...
Rian Rizvi
  • 9,989
  • 2
  • 37
  • 33
  • 2
    the only problem with this is that it's really not neat at all, and whenever you want to use this pattern you have to cut & paste the code... hence my use of a decorator – Claudiu Jan 30 '13 at 00:07
  • 2
    probably should use something like `try: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0` – endolith Dec 06 '13 at 02:26
  • 2
    Please use `X not in Y` rather than `not X in Y` (or advise using if you were just using it for the sake of a more similar-looking comparison between that and `hasattr`) – Nick T Sep 30 '14 at 19:09
  • how about this: `def fn(): if not hasattr(fn, 'c'): fn.c = 0` `fn.c += 1 return fn.c` – MANU Dec 18 '17 at 05:11
  • it's not ideal because the if clause adds unnecessary nesting, in this situation I prefer setdefault – Rian Rizvi Dec 18 '17 at 18:36
  • @RiazRizvi: [Jonathan's answer](https://stackoverflow.com/a/27783657/364696) fixes that issue; `getattr` is an all-in-one version of what you are doing with `vars`+`setdefault`, and a more direct/clear way of doing so (since you're trying to get an attribute, use `getattr`[ibute]). – ShadowRanger Dec 28 '19 at 02:52
18

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
  • 77,584
  • 5
  • 64
  • 78
18

Other solutions attach a counter attribute to the function, usually with convoluted logic to handle the initialization. This is inappropriate for new code.

In Python 3, the right way is to use a nonlocal statement:

counter = 0
def foo():
    nonlocal counter
    counter += 1
    print(f'counter is {counter}')

See PEP 3104 for the specification of the nonlocal statement.

If the counter is intended to be private to the module, it should be named _counter instead.

cbarrick
  • 1,344
  • 12
  • 17
  • 5
    Even before Python 3, you could always do this with a `global counter` statement instead of `nonlocal counter` (`nonlocal` just lets you write to closure state in a nested function). The reason people are attaching an attribute to the function is to avoid polluting the global namespace for state that's specific to the function, so you don't have to do even hackier things when two functions need independent `counter`s. This solution doesn't scale; attributes on the function do. [kdb's answer](https://stackoverflow.com/a/31784304/364696) is how `nonlocal` can help, but it does add complexity. – ShadowRanger Dec 28 '19 at 02:57
  • Eh, I think the complexity of a factory function or decorator is overkill unless you're doing this a lot, and in that case the design is already a bit smelly. For a one-off, just add the nonlocal counter and be done with it. I've added a bit to the answer about naming conventions. Also, the reason I recommend `nonlocal` over `global` is exactly as you point out -- it works in strictly more circumstances. – cbarrick Jan 03 '20 at 04:28
  • 1
    I've got this in PyCharm: Nonlocal variable '_counter' must be bound in an outer function scope – ishahak May 11 '22 at 09:04
  • @ishahak please try to create a [mre], and then ask a new question if you cannot [find](https://meta.stackoverflow.com/questions/261592) an existing one. – Karl Knechtel Jul 03 '22 at 07:09
12

Using an attribute of a function as static variable has some potential drawbacks:

  • Every time you want to access the variable, you have to write out the full name of the function.
  • Outside code can access the variable easily and mess with the value.

Idiomatic python for the second issue would probably be naming the variable with a leading underscore to signal that it is not meant to be accessed, while keeping it accessible after the fact.

Using closures

An alternative would be a pattern using lexical closures, which are supported with the nonlocal keyword in python 3.

def make_counter():
    i = 0
    def counter():
        nonlocal i
        i = i + 1
        return i
    return counter
counter = make_counter()

Sadly I know no way to encapsulate this solution into a decorator.

Using an internal state parameter

Another option might be an undocumented parameter serving as a mutable value container.

def counter(*, _i=[0]):
    _i[0] += 1
    return _i[0]

This works, because default arguments are evaluated when the function is defined, not when it is called.

Cleaner might be to have a container type instead of the list, e.g.

def counter(*, _i = Mutable(0)):
    _i.value += 1
    return _i.value

but I am not aware of a builtin type, that clearly communicates the purpose.

kdb
  • 4,098
  • 26
  • 49
  • This limbo condition of the internal state parameter makes me think of C++'s hidden friend idiom. – 303 Oct 21 '21 at 18:39
10
_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
  • 10,369
  • 1
  • 38
  • 35
10

A little bit more readable, but more verbose (Zen of Python: explicit is better than implicit):

>>> def func(_static={'counter': 0}):
...     _static['counter'] += 1
...     print _static['counter']
...
>>> func()
1
>>> func()
2
>>>

See here for an explanation of how this works.

warvariuc
  • 57,116
  • 41
  • 173
  • 227
  • can you elaborate on why this code works? The second `foo()` should re-initialize the dictionary to the value specified in the function definition (so with the counter key having a value of 0). Why it does not? – robertspierre Jul 06 '17 at 09:49
  • 5
    @raffamaiden: Default arguments are evaluated only once when the function is defined and not each time the function is called. – Daniel K. Jun 20 '19 at 11:08
7
def staticvariables(**variables):
    def decorate(function):
        for variable in variables:
            setattr(function, variable, variables[variable])
        return function
    return decorate

@staticvariables(counter=0, bar=1)
def foo():
    print(foo.counter)
    print(foo.bar)

Much like vincent's code above, this would be used as a function decorator and static variables must be accessed with the function name as a prefix. The advantage of this code (although admittedly anyone might be smart enough to figure it out) is that you can have multiple static variables and initialise them in a more conventional manner.

Rafa Viotti
  • 9,998
  • 4
  • 42
  • 62
6

After trying several approaches I ended up using an improved version of @warvariuc's answer:

import types

def func(_static=types.SimpleNamespace(counter=0)):
    _static.counter += 1
    print(_static.counter)
VPfB
  • 14,927
  • 6
  • 41
  • 75
5

A static variable inside a Python method

class Count:
    def foo(self):
        try: 
            self.foo.__func__.counter += 1
        except AttributeError: 
            self.foo.__func__.counter = 1

        print self.foo.__func__.counter

m = Count()
m.foo()       # 1
m.foo()       # 2
m.foo()       # 3
wannik
  • 12,212
  • 11
  • 46
  • 58
5

A global declaration provides this functionality. In the example below (python 3.5 or greater to use the "f"), the counter variable is defined outside of the function. Defining it as global in the function signifies that the "global" version outside of the function should be made available to the function. So each time the function runs, it modifies the value outside the function, preserving it beyond the function.

counter = 0

def foo():
    global counter
    counter += 1
    print("counter is {}".format(counter))

foo() #output: "counter is 1"
foo() #output: "counter is 2"
foo() #output: "counter is 3"
  • This works the same way if used correctly. The difference to the c-code is that in the OP's c example, the counter variable could only be touched by the function. A global variable in python may be used or altered anywhere in the script – MortenSickel May 11 '19 at 10:53
5

The idiomatic way is to use a class, which can have attributes. If you need instances to not be separate, use a singleton.

There are a number of ways you could fake or munge "static" variables into Python (one not mentioned so far is to have a mutable default argument), but this is not the Pythonic, idiomatic way to do it. Just use a class.

Or possibly a generator, if your usage pattern fits.

Teddy
  • 6,013
  • 3
  • 26
  • 38
4

Another (not recommended!) twist on the callable object like https://stackoverflow.com/a/279598/916373, if you don't mind using a funky call signature, would be to do

class foo(object):
    counter = 0;
    @staticmethod
    def __call__():
        foo.counter += 1
        print "counter is %i" % foo.counter

>>> foo()()
counter is 1
>>> foo()()
counter is 2
Community
  • 1
  • 1
lost
  • 2,210
  • 2
  • 20
  • 34
4

Soulution n +=1

def foo():
  foo.__dict__.setdefault('count', 0)
  foo.count += 1
  return foo.count
Feca
  • 41
  • 1
4

Using a decorator and a closure

The following decorator can be used create static function variables. It replaces the declared function with the return from itself. This implies that the decorated function must return a function.

def static_inner_self(func):
    return func()

Then use the decorator on a function that returns another function with a captured variable:

@static_inner_self
def foo():
    counter = 0
    def foo():
        nonlocal counter
        counter += 1
        print(f"counter is {counter}")
    return foo

nonlocal is required, otherwise Python thinks that the counter variable is a local variable instead of a captured variable. Python behaves like that because of the variable assignment counter += 1. Any assignment in a function makes Python think that the variable is local.

If you are not assigning to the variable in the inner function, then you can ignore the nonlocal statement, for example, in this function I use to indent lines of a string, in which Python can infer that the variable is nonlocal:

@static_inner_self
def indent_lines():
    import re
    re_start_line = re.compile(r'^', flags=re.MULTILINE)
    def indent_lines(text, indent=2):
        return re_start_line.sub(" "*indent, text)
    return indent_lines

P.S. There is a deleted answer that proposed the same. I don't know why the author deleted it. https://stackoverflow.com/a/23366737/195417

Miguel Angelo
  • 23,796
  • 16
  • 59
  • 82
3

Prompted by this question, may I present another alternative which might be a bit nicer to use and will look the same for both methods and functions:

@static_var2('seed',0)
def funccounter(statics, add=1):
    statics.seed += add
    return statics.seed

print funccounter()       #1
print funccounter(add=2)  #3
print funccounter()       #4

class ACircle(object):
    @static_var2('seed',0)
    def counter(statics, self, add=1):
        statics.seed += add
        return statics.seed

c = ACircle()
print c.counter()      #1
print c.counter(add=2) #3
print c.counter()      #4
d = ACircle()
print d.counter()      #5
print d.counter(add=2) #7
print d.counter()      #8    

If you like the usage, here's the implementation:

class StaticMan(object):
    def __init__(self):
        self.__dict__['_d'] = {}

    def __getattr__(self, name):
        return self.__dict__['_d'][name]
    def __getitem__(self, name):
        return self.__dict__['_d'][name]
    def __setattr__(self, name, val):
        self.__dict__['_d'][name] = val
    def __setitem__(self, name, val):
        self.__dict__['_d'][name] = val

def static_var2(name, val):
    def decorator(original):
        if not hasattr(original, ':staticman'):    
            def wrapped(*args, **kwargs):
                return original(getattr(wrapped, ':staticman'), *args, **kwargs)
            setattr(wrapped, ':staticman', StaticMan())
            f = wrapped
        else:
            f = original #already wrapped

        getattr(f, ':staticman')[name] = val
        return f
    return decorator
Community
  • 1
  • 1
Claudiu
  • 224,032
  • 165
  • 485
  • 680
3

Instead of creating a function having a static local variable, you can always create what is called a "function object" and give it a standard (non-static) member variable.

Since you gave an example written C++, I will first explain what a "function object" is in C++. A "function object" is simply any class with an overloaded operator(). Instances of the class will behave like functions. For example, you can write int x = square(5); even if square is an object (with overloaded operator()) and not technically not a "function." You can give a function-object any of the features that you could give a class object.

# C++ function object
class Foo_class {
    private:
        int counter;     
    public:
        Foo_class() {
             counter = 0;
        }
        void operator() () {  
            counter++;
            printf("counter is %d\n", counter);
        }     
   };
   Foo_class foo;

In Python, we can also overload operator() except that the method is instead named __call__:

Here is a class definition:

class Foo_class:
    def __init__(self): # __init__ is similair to a C++ class constructor
        self.counter = 0
        # self.counter is like a static member
        # variable of a function named "foo"
    def __call__(self): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
foo = Foo_class() # call the constructor

Here is an example of the class being used:

from foo import foo

for i in range(0, 5):
    foo() # function call

The output printed to the console is:

counter is 1
counter is 2
counter is 3
counter is 4
counter is 5

If you want your function to take input arguments, you can add those to __call__ as well:

# FILE: foo.py - - - - - - - - - - - - - - - - - - - - - - - - -

class Foo_class:
    def __init__(self):
        self.counter = 0
    def __call__(self, x, y, z): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
        print("x, y, z, are %d, %d, %d" % (x, y, z));
foo = Foo_class() # call the constructor

# FILE: main.py - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

from foo import foo

for i in range(0, 5):
    foo(7, 8, 9) # function call

# Console Output - - - - - - - - - - - - - - - - - - - - - - - - - - 

counter is 1
x, y, z, are 7, 8, 9
counter is 2
x, y, z, are 7, 8, 9
counter is 3
x, y, z, are 7, 8, 9
counter is 4
x, y, z, are 7, 8, 9
counter is 5
x, y, z, are 7, 8, 9
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42
2

This answer builds on @claudiu 's answer.

I found that my code was getting less clear when I always had to prepend the function name, whenever I intend to access a static variable.

Namely, in my function code I would prefer to write:

print(statics.foo)

instead of

print(my_function_name.foo)

So, my solution is to :

  1. add a statics attribute to the function
  2. in the function scope, add a local variable statics as an alias to my_function.statics
from bunch import *

def static_vars(**kwargs):
    def decorate(func):
        statics = Bunch(**kwargs)
        setattr(func, "statics", statics)
        return func
    return decorate

@static_vars(name = "Martin")
def my_function():
    statics = my_function.statics
    print("Hello, {0}".format(statics.name))

Remark

My method uses a class named Bunch, which is a dictionary that supports attribute-style access, a la JavaScript (see the original article about it, around 2000)

It can be installed via pip install bunch

It can also be hand-written like so:

class Bunch(dict):
    def __init__(self, **kw):
        dict.__init__(self,kw)
        self.__dict__ = self
Pascal T.
  • 3,866
  • 4
  • 33
  • 36
  • Note: `types.SimpleNamespace` (available since 3.3) supports this behavior out of the box (and is implemented in C on CPython, so it's about as fast as it can be). – ShadowRanger Dec 28 '19 at 03:12
1

I personally prefer the following to decorators. To each their own.

def staticize(name, factory):
    """Makes a pseudo-static variable in calling function.

    If name `name` exists in calling function, return it. 
    Otherwise, saves return value of `factory()` in 
    name `name` of calling function and return it.

    :param name: name to use to store static object 
    in calling function
    :type name: String
    :param factory: used to initialize name `name` 
    in calling function
    :type factory: function
    :rtype: `type(factory())`

    >>> def steveholt(z):
    ...     a = staticize('a', list)
    ...     a.append(z)
    >>> steveholt.a
    Traceback (most recent call last):
    ...
    AttributeError: 'function' object has no attribute 'a'
    >>> steveholt(1)
    >>> steveholt.a
    [1]
    >>> steveholt('a')
    >>> steveholt.a
    [1, 'a']
    >>> steveholt.a = []
    >>> steveholt.a
    []
    >>> steveholt('zzz')
    >>> steveholt.a
    ['zzz']

    """
    from inspect import stack
    # get scope enclosing calling function
    calling_fn_scope = stack()[2][0]
    # get calling function
    calling_fn_name = stack()[1][3]
    calling_fn = calling_fn_scope.f_locals[calling_fn_name]
    if not hasattr(calling_fn, name):
        setattr(calling_fn, name, factory())
    return getattr(calling_fn, name)
spenthil
  • 3,373
  • 29
  • 17
  • 3
    Please don't be offended, but this solution reminds me a bit of the "large company style" :-) http://www.willa.me/2013/11/the-six-most-common-species-of-code.html – JJC Nov 19 '13 at 15:50
  • Yes, using non-portable (stack manipulation in general is a CPython implementation detail, not something you can rely on in PyPy, Jython, IronPython, what-have-you), fragile stack manipulation, with half a dozen function calls on every use is *way* better than a simple decorator... – ShadowRanger Dec 28 '19 at 03:05
0

Building on Daniel's answer (additions):

class Foo(object): 
    counter = 0  

def __call__(self, inc_value=0):
    Foo.counter += inc_value
    return Foo.counter

foo = Foo()

def use_foo(x,y):
    if(x==5):
        foo(2)
    elif(y==7):
        foo(3)
    if(foo() == 10):
        print("yello")


use_foo(5,1)
use_foo(5,1)
use_foo(1,7)
use_foo(1,7)
use_foo(1,1)

The reason why I wanted to add this part is , static variables are used not only for incrementing by some value, but also check if the static var is equal to some value, as a real life example.

The static variable is still protected and used only within the scope of the function use_foo()

In this example, call to foo() functions exactly as(with respect to the corresponding c++ equivalent) :

stat_c +=9; // in c++
foo(9)  #python equiv

if(stat_c==10){ //do something}  // c++

if(foo() == 10):      # python equiv
  #add code here      # python equiv       

Output :
yello
yello

if class Foo is defined restrictively as a singleton class, that would be ideal. This would make it more pythonic.

yash
  • 19
  • 2
0

I write a simple function to use static variables:

def Static():
    ### get the func object by which Static() is called.
    from inspect import currentframe, getframeinfo
    caller = currentframe().f_back
    func_name = getframeinfo(caller)[2]
    # print(func_name)
    caller = caller.f_back
    func = caller.f_locals.get(
        func_name, caller.f_globals.get(
            func_name
        )
    )
    
    class StaticVars:
        def has(self, varName):
            return hasattr(self, varName)
        def declare(self, varName, value):
            if not self.has(varName):
                setattr(self, varName, value)

    if hasattr(func, "staticVars"):
        return func.staticVars
    else:
        # add an attribute to func
        func.staticVars = StaticVars()
        return func.staticVars

How to use:

def myfunc(arg):
    if Static().has('test1'):
        Static().test += 1
    else:
        Static().test = 1
    print(Static().test)

    # declare() only takes effect in the first time for each static variable.
    Static().declare('test2', 1)
    print(Static().test2)
    Static().test2 += 1
0x262f
  • 36
  • 3
0

Miguel Angelo's self-redefinition solution is even possible without any decorator:

def fun(increment=1):
    global fun
    counter = 0
    def fun(increment=1):
        nonlocal counter
        counter += increment
        print(counter)
    fun(increment)

fun()    #=> 1
fun()    #=> 2
fun(10)  #=> 12

The second line has to be adapted to get a limited scope:

def outerfun():
    def innerfun(increment=1):
        nonlocal innerfun
        counter = 0
        def innerfun(increment=1):
            nonlocal counter
            counter += increment
            print(counter)
        innerfun(increment)

    innerfun()    #=> 1
    innerfun()    #=> 2
    innerfun(10)  #=> 12

outerfun()

The plus of the decorator is that you don't have to pay extra attention to the scope of your construction.

0

I don't know how future-proof this solution is, so maybe it's only a curiosity. It exploits a python-gotcha that default parameters are only evaluated once.

def foo(counter=[0]):
    counter[0] += 1
    print(f"{counter[0] = }")

foo()
foo()

output is

counter[0] = 1
counter[0] = 2

obvious variants are

def foo(thing,counter={}):
    if thing not in counter:
        counter[thing] = 0
    counter[thing] += 1
    print(f"count of {thing} = {counter[thing]}")
    return counter

foo('cat')
foo('cat')
foo('bird')

output is

count of cat = 1
count of cat = 2
count of bird = 1

def fib(answer=[]):
    if len(answer)<2: # empty lists are not True
        answer.append(1)
    else:
        answer.append(answer[-2]+answer[-1])
    return answer[-1]

print(f"{[fib() for i in range(10)]}")

output is

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]    
Haydon Berrow
  • 485
  • 2
  • 6
  • 14
0

Variant of the class method, makes the calling signature easier. using init means you don't need to use the () you need for _call, or create an instance of the class.

class do_work:
    cnt=0

    @staticmethod
    def __init__(arg):
        do_work.cnt += 1
        print(arg, do_work.cnt)

do_work('a')
do_work('b')
do_work('e')
kdubs
  • 1,596
  • 1
  • 21
  • 36
-2

Sure this is an old question but I think I might provide some update.

It seems that the performance argument is obsolete. The same test suite appears to give similar results for siInt_try and isInt_re2. Of course results vary, but this is one session on my computer with python 3.4.4 on kernel 4.3.01 with Xeon W3550. I have run it several times and the results seem to be similar. I moved the global regex into function static, but the performance difference is negligible.

isInt_try: 0.3690
isInt_str: 0.3981
isInt_re: 0.5870
isInt_re2: 0.3632

With performance issue out of the way, it seems that try/catch would produce the most future- and cornercase- proof code so maybe just wrap it in function

Keji Li
  • 62
  • 1
  • 7
  • 2
    What are you even comparing here? This seems like a comment on other answers, but it's not clear which ones, and it doesn't answer the question itself. – ShadowRanger Dec 28 '19 at 03:08