An oft-asked question is whether there is an equivalent to static variables inside functions in Python. There are many answers, such as creating wrapper classes, using nested functions, decorators, etc.
One of the most elegant solutions I found was this, which I have slightly modified:
def foo():
# see if foo.counter already exists
try: test = foo.counter
# if not, initialize it to whatever
except AttributeError: foo.counter = 0
# do stuff with foo.counter
.....
.....
Example:
static.py
def foo(x):
# see if foo.counter already exists
try: test = foo.counter
# if not, initialize it to whatever
except AttributeError: foo.counter = 0
foo.counter += x
print(foo.counter)
for i in range(10):
foo(i)
output
$ python static.py
0
1
3
6
10
15
21
28
36
45
Is there any reason I should avoid this method? How the heck does it work, anyway?
It works because the function's name is just another entry in the local scope, and the function is an object like everything else in Python, and can have arbitrary attributes set on it:
def foo():
# The foo function object has already been fully constructed
# by the time we get into our `try`
try: test = foo.counter # Only run when foo is invoked
except AttributeError: foo.counter = 0
foo.counter += 1
if hasattr(foo, 'counter'):
print('Foo has a counter attribute')
else:
# This prints out - we've parsed `foo` but not executed it yet
print('Foo.counter does not exist yet')
# Now, we invoke foo
foo()
if hasattr(foo, 'counter'):
# And from now on (until we remove the attribute)
# this test will always succeed because we've added the counter
# attribute to the function foo.
print('Foo has a counter attribute')
else:
print('Foo.counter does not exist yet') # No longer true
Why not this:
def foo(x):
foo.counter += x
print(foo.counter)
foo.counter = 0 # init on module import
And then:
for i in range(10):
foo(i)
I get the same output with py2.7, py3.4.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With