when i write a function that needs to keep state (such as a count of the number of times it has been called) what i have been doing is putting a variable in the global name space and working with that. one problem i have been worrying about is name space conflict. can you suggest the best way to avoid that problem? is there a better way to make a function that can keep state between calls w/o using globals or classes?
classes are pretty powerful. They have many different uses.
@classmethod and @staticmethod could achieve this pretty easily.
class man:
x = 0
@classmethod
def working(cls, value):
cls.x += value
print(cls.x)
man.working(8)
man.working(7)
man.working(8)
Generators are keeping the state between calls.
Here are 3 solutions: the first one uses a function object
spam
, the second one stores an attribute in the function itself
eggs
, and the third one uses the parameter list to store a static object, using the fact that the parameter list is initialized at function's definition
ham
class spam:
__name__ = 'spam'
x = 0
def __call__(self, value):
self.x += value
return self.x
spam = spam()
def run(func):
for v in (8, 7, 8):
print('{}({}) --> {}'.format(func.__name__,v, func(v)))
print()
run(spam)
def eggs(value):
eggs.x += value
return eggs.x
eggs.x = 0
run(eggs)
class Static:
def __init__(self, *args, **kwargs):
self.__dict__.update(*args, **kwargs)
def ham(value, static=Static(x=0)):
static.x += value
return static.x
run(ham)
The output is
Output:
spam(8) --> 8
spam(7) --> 15
spam(8) --> 23
eggs(8) --> 8
eggs(7) --> 15
eggs(8) --> 23
ham(8) --> 8
ham(7) --> 15
ham(8) --> 23
Here two more options
class Counter:
counter = {}
@classmethod
def grab(cls, key):
cls.counter[key] = cls.counter.get(key, 0) + 1
return cls.counter[key]
def myfunc():
counter = Counter.grab('x')
print(counter)
myfunc()
myfunc()
myfunc()
class Counter:
pass
def myfunc():
try:
Counter.x += 1
except:
Counter.x = 1
print(Counter.x)
myfunc()
myfunc()
myfunc()
Probably a better option is just to profile your code.
python -m cProfile yourcode.py
It tells you how many times a function been called.
profile of mycode above
Output:
25 function calls in 0.000 seconds
Ordered by: call count
ncalls tottime percall cumtime percall filename:lineno(function)
6 0.000 0.000 0.000 0.000 {built-in method builtins.print}
3 0.000 0.000 0.000 0.000 help6.py:5(grab)
3 0.000 0.000 0.000 0.000 help6.py:21(myfunc)
3 0.000 0.000 0.000 0.000 help6.py:10(myfunc)
3 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
2 0.000 0.000 0.000 0.000 {built-in method builtins.__build_class__}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 help6.py:18(Counter)
1 0.000 0.000 0.000 0.000 help6.py:2(Counter)
1 0.000 0.000 0.000 0.000 help6.py:2(<module>)
1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}
counting the number of times a function is called was an example of saving state. it is not what i am trying the achieve.
what i want to do is save information/data between function calls. i have been using classes. i was wondering if there was also another way that could, perhaps, be simpler. i didn't think about generators, but yeah, as a separate context that persists, they have to be saving state.