Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this Python "static variable" hack ok to use? [closed]

Tags:

python

static

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?

like image 784
abalter Avatar asked Apr 01 '16 04:04

abalter


2 Answers

How does this work?

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
like image 168
Sean Vieira Avatar answered Oct 20 '22 06:10

Sean Vieira


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.

like image 45
Nick Avatar answered Oct 20 '22 06:10

Nick