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)
Answer from Claudiu on Stack OverflowA 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)
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.
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...
You can create a closure with nonlocal to make them editable (python 3.x only). Here's an example of a recursive function to calculate the length of a list.
def recursive_len(l):
res = 0
def inner(l2):
nonlocal res
if l2:
res += 1
inner(l2[1:])
inner(l)
return res
Or, you can assign an attribute to the function itself. Using the trick from here:
def fn(self):
self.number_of_times += 1
fn.func_defaults = (fn,)
fn.number_of_times = 0
fn()
fn()
fn()
print (fn.number_of_times)
I really don't think
Valueis a help here.
Whilst I can see the allure of only assigningxtostatic.x. This just looks like a smart, but not exactly good, work around to get default values.You can just have
with_staticstake keyword arguments as the defaults.def with_statics(**defaults): statics = Statics(defaults) # Implementation left to imagination def inner(f): @wraps(f) def wrapper(*args, **kwargs): return f(statics, *args, **kwargs) return wrapper return innerDefining
Valueinside of two other classes is not great. You can just move it to the global scope and change theisinstancecheck to not need to use indexing.If you still want the class on the other classes then you can just assign it as a class attribute.
class Current: Value = Value ...I think I'm fairly well versed in the magical Python arts. However your code is just confusing.
Your code looks like it's a hack when a hack isn't needed.
To highlight this I will convert your doctests to not use your code.
def counter():
counter.val += 1
return counter.val
counter.val = 0
print(counter(), counter(), counter())
def get_string():
print("Getting string")
return ""
def record(text):
if record.recorded is None:
record.recorded = get_string()
record.recorded += text
return record.recorded
record.recorded = None
print(record("Hello"))
print(record(", world!"))
Yes it works. However, honestly, why would you want this?
In the few cases I've wanted C-style static variables, I've used a Class with a __call__() method:
class Foo:
a = 1
b = 2
def __call__(self):
print(self.a, self.b)
foo = Foo()
foo()
Or, you could use a function decorator
def staticvars(**vars):
def f(func):
for k,v in vars.items():
setattr(func, k, v)
return func
return f
@staticvars(a=1,b=2)
def foo():
print(foo.a, foo.b)
Or, under the idea "it's easier to get forgiveness than permission":
def counter():
try:
counter.x += 1
except AttributeError:
counter.x = 1
return counter.x
print(counter(), counter(), counter()) # prints: 1, 2, 3