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)
Assuming what you want is "a variable that is initialised only once on first function call", there's no such thing in Python syntax. But there are ways to get a similar result:
1 - Use a global. Note that in Python, 'global' really means 'global to the module', not 'global to the process':
_number_of_times = 0
def yourfunc(x, y):
global _number_of_times
for i in range(x):
for j in range(y):
_number_of_times += 1
2 - Wrap you code in a class and use a class attribute (ie: an attribute that is shared by all instances). :
class Foo(object):
_number_of_times = 0
@classmethod
def yourfunc(cls, x, y):
for i in range(x):
for j in range(y):
cls._number_of_times += 1
Note that I used a classmethod
since this code snippet doesn't need anything from an instance
3 - Wrap you code in a class, use an instance attribute and provide a shortcut for the method:
class Foo(object):
def __init__(self):
self._number_of_times = 0
def yourfunc(self, x, y):
for i in range(x):
for j in range(y):
self._number_of_times += 1
yourfunc = Foo().yourfunc
4 - Write a callable class and provide a shortcut:
class Foo(object):
def __init__(self):
self._number_of_times = 0
def __call__(self, x, y):
for i in range(x):
for j in range(y):
self._number_of_times += 1
yourfunc = Foo()
4 bis - use a class attribute and a metaclass
class Callable(type):
def __call__(self, *args, **kw):
return self._call(*args, **kw)
class yourfunc(object):
__metaclass__ = Callable
_numer_of_times = 0
@classmethod
def _call(cls, x, y):
for i in range(x):
for j in range(y):
cls._number_of_time += 1
5 - Make a "creative" use of function's default arguments being instantiated only once on module import:
def yourfunc(x, y, _hack=[0]):
for i in range(x):
for j in range(y):
_hack[0] += 1
There are still some other possible solutions / hacks, but I think you get the big picture now.
EDIT: given the op's clarifications, ie "Lets say you have a recursive function with default parameter but if someone actually tries to give one more argument to your function it could be catastrophic", it looks like what the OP really wants is something like:
# private recursive function using a default param the caller shouldn't set
def _walk(tree, callback, level=0):
callback(tree, level)
for child in tree.children:
_walk(child, callback, level+1):
# public wrapper without the default param
def walk(tree, callback):
_walk(tree, callback)
Which, BTW, prove we really had Yet Another XY Problem...
Best Answer
Variables defined at the class level are indeed "static", but I don't think they work quite the way you think they do. There are 2 levels here which you need to worry about. There are attributes at the class level, and there are attributes at the instance level. Whenever you do
self.attribute = ...
inside a method, you're setting an attribute at the instance level. Whenever python looks up an attribute, it first looks at the instance level, if it doesn't find the attribute, it looks at the class level.This can be a little confusing (especially if the attribute is a reference to a mutable object). consider:
As others have mentioned, if you want an attribute to be specific to an instance, just initialize it as an instance attribute in
__init__
(usingself.attr = ...
).__init__
is a special method which is run whenever a class is initialized (with a few exceptions that we won't discuss here).e.g.