49

There is a questions asking how to simulate static variables in python.

Also, on the web one can find many different solutions to create static variables. (Though I haven't seen one that I like yet.)

Why doesn't Python support static variables in methods? Is this considered unpythonic or has it something to do with Python's syntax?

Edit:

I asked specifically about the why of the design decision and I haven't provided any code example because I wanted to avoid explanation to simulate static variables.

2
  • What was wrong with the answers to stackoverflow.com/questions/460586/…? Why re-ask the question? Commented Feb 27, 2009 at 16:57
  • 6
    I explicitly didn't ask how to simulate it, but what the reason for this decision are. Commented Feb 27, 2009 at 19:22

10 Answers 10

83

The idea behind this omission is that static variables are only useful in two situations: when you really should be using a class and when you really should be using a generator.

If you want to attach stateful information to a function, what you need is a class. A trivially simple class, perhaps, but a class nonetheless:

def foo(bar):
    static my_bar # doesn't work

    if not my_bar:
        my_bar = bar

    do_stuff(my_bar)

foo(bar)
foo()

# -- becomes ->

class Foo(object):
    def __init__(self, bar):
        self.bar = bar

    def __call__(self):
        do_stuff(self.bar)

foo = Foo(bar)
foo()
foo()

If you want your function's behavior to change each time it's called, what you need is a generator:

def foo(bar):
    static my_bar # doesn't work

    if not my_bar:
        my_bar = bar

    my_bar = my_bar * 3 % 5

    return my_bar

foo(bar)
foo()

# -- becomes ->

def foogen(bar):
    my_bar = bar

    while True:
        my_bar = my_bar * 3 % 5
        yield my_bar

foo = foogen(bar)
foo.next()
foo.next()

Of course, static variables are useful for quick-and-dirty scripts where you don't want to deal with the hassle of big structures for little tasks. But there, you don't really need anything more than global — it may seem a but kludgy, but that's okay for small, one-off scripts:

def foo():
    global bar
    do_stuff(bar)

foo()
foo()
Sign up to request clarification or add additional context in comments.

9 Comments

Ok for the fact that you indeed might be needing a class. Apart for the ugly Borg pattern, there is no real way to ensure this class is a singleton. Actually the importation of package makes it quite a pain.
The class sample does not look like it is Python. I think you meant: "private bar" => "bar = None", and in init you'd have "self.my_bar = bar".
@Heikki Toivonen: Agreed, also in the first example "my_bar" doesn't have to be a class variable, an instance variable would work just as well.
@Paul: Use only class-level variables and @classmethod decorators and your class is also a singleton object.
+1 for concept, the python code needs to be cleaned as the above comments have said :)
|
20

One alternative to a class is a function attribute:

def foo(arg):
    if not hasattr(foo, 'cache'):
        foo.cache = get_data_dict()
    return foo.cache[arg]

While a class is probably cleaner, this technique can be useful and is nicer, in my opinion, then a global.

4 Comments

What I don't like about it is the need write the method name over and over. If I changed the name I'd have to rewrite half the code.
@gs: Yes, this is a shortcoming. I was looking for some way to reference the current function instead of using the name (something like self) but I don't think there is such a way. You could do a "this = foo" at the very top and then reference "this" everywhere so a rename would be easy to maintain.
This works as is, but you're really setting yourself up for trouble when someone passes foo around and receives NameError: global name 'foo' is not defined
@jleedev: The globals used (which contain the name "foo") are the module globals from when the function is created, not where it's called from (that would imply dynamic scoping, rather than lexical). Passing it around won't make any difference.
7

In Python 3, I would use a closure:

def makefoo():
    x = 0
    def foo():
        nonlocal x
        x += 1
        return x
    return foo

foo = makefoo()

print(foo())
print(foo())

1 Comment

hello i am still trying to figure out how this works. Can u plz explain a little further? Thanks in advance. :)
6

I think most uses of local static variables is to simulate generators, that is, having some function which performs some iteration of a process, returns the result, but mantains the state for the subsequent invocation. Python handles this very elegantly using the yield command, so it seems there is not so much need for static variables.

1 Comment

I'd like to use them to cache things loaded from disk. I think it clutters the instance less, if I could assign them to the function.
5

It's a design choice.

I'm assuming Guido thinks you don't need them very often, and you never really need them: you can always just use a global variable and tell everyone to keep their greasy paws offa' your variable ;-)

2 Comments

uh, or you could pass objects around as normal.
suppose you want to use your own random generator in quicksort. Should callers pass in the randomness? No. Should it be updated between (otherwise independent) calls? Yes. Sometimes you don't want to pass around objects...
4

For caching or memoization purposes, decorators can be used as an elegant and general solution.

Comments

2

The question is very old, but I think, the correct answer has not been given.

The actual reason for Python as a programming language to not have C-style static variables, seems to be a very personal issue of Guido van Rossum. To me, it seems he likely found the distinction between lifetime scope and visibility scope too complicated or inconvenient. Except for maybe special cases that I am not aware of, Python does not have encapsulation of variables in functions or classes.

How does visibility or encapsulation relate to static variables?

Static variables behave to global variables like private members behave to public members. Sure, there is name mangling but the variable is still accessible and it doesn't provide actual encapsulation. Nested static variables convey information and guarantees of inaccessibility which non-static variables do not.

Same story for C-style static variables. They have global scope but are private to the function, file or code scope in which they are defined. It's the same concept as a private static variable in OOP, just that it can be defined in functions. In Python, however, when a static class variable is accessible to one function, it is also accessible to others. There is no fine-grain setting to control visibility of certain variables.

Generators or classes do not convey the full meaning of static variables

The coherence (belongingness) between a variable and a unit of code can certainly not be expressed in the same way using global variables. Generators and classes are not static either and without being static, they suffer from the same problem as non-static variables: access to them cannot be restricted to a certain region of code. In order to use a generator or class instance, you need to carry it around, and it is gone as soon as it goes out of scope.

A global variable does not convey the same meaning as a C-style static variable, the meaning of only being used by some limited region of code. Effectively, global variables are much more difficult to refactor than static variables.

Design options

Guido actually had two options for getting rid of having two different scopes, by either removing the concept of lifetime scope or the concept of visibility scope.

I could find more arguments for removing lifetime scope (memory management) over visibility scope (data protection), being easier to use, better maintainable and easier to understand or learn. In analogy, words in natural language do not have a lifetime either, just context and visibility.

But removing visibility is easier to implement and rather conservative. The first languages did not have a visibility concept at all, such as machine and Assembly languages. And extra visibility scoping is simply the option that Guido decided to remove. Therefore, you still have keywords such as del which is probably a remnant of old-school manual memory management instead of managing variables by visibility concepts.

What can we do about it?

And since others provided their way of simulating static variables, here is mine:

def foo():
    global bar
    if 'bar' not in globals(): bar = 0 # default value
    # use `bar` here

It makes trouble when the name is also used by other functions, but as there is no encapsulation anyways, it might be a good idea to express coherence better by using specialized names.

1 Comment

Thanks for taking the time to write out your answer!
1

The answer's pretty much the same as why nobody uses static methods (even though they exist). You have a module-level namespace that serves about the same purpose as a class would anyway.

Comments

0

An ill-advised alternative:

You can also use the side-effects of the definition time evaluation of function defaults:

def func(initial=0, my_static=[])
  if not my_static:
    my_static.append(initial)

   my_static[0] += 1
  return my_static[0]

print func(0), func(0), func(0)

Its really ugly and easily subverted, but works. Using global would be cleaner than this, imo.

Comments

-1

From one of your comments: "I'd like to use them to cache things loaded from disk. I think it clutters the instance less, if I could assign them to the function"

Use a caching class then, as a class or instance attribute to your other class. That way, you can use the full feature set of classes without cluttering other things. Also, you get a reusable tool.

This shows that on SO it always pays off to state one's problem instead of asking for a specific, low level solution (e.g. for a missing language feature). That way, instead of endless debates about simulating "static" (a deprecated feature from an ancient language, in my view) someone could have given a good answer to you problem sooner.

1 Comment

I knew how to simulate them. The question was specifically about the decision.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.