I'm trying to implement a so-called static variable in my method, similar to the decorator method described in this Stackoverflow thread. Specifically, I define a decorator function as follows:
def static_var(varName, value):
def decorate(function):
setattr(function,varName,value)
return function
return decorate
As the example shows, this can be used to attach a variable to the function:
@static_var('seed', 0)
def counter():
counter.seed +=1
return counter.seed
This method will return the number of times it has been called.
The issue i am having is that this does not work if I define the method inside a class:
class Circle(object):
@static_var('seed',0)
def counter(self):
counter.seed +=1
return counter.seed
If I instantiate a Circle
and run counter
,
>>>> myCircle = Circle()
>>>> myCircle.counter()
I get the following error: NameError: global name 'counter' is not defined
.
My response to this was that maybe I need to use self.counter
, i.e.
class Circle(object):
@static_var('seed',0)
def counter(self):
self.counter.seed +=1
return self.counter.seed
However this produces the error, AttributeError: 'instancemethod' object has no attribute 'seed'
.
What is going on here?
What you're trying to achieve looks like something you shouldn't be doing at all.
In the first case, you can just as easily get away with a much simpler:
And in the second case, you can just as easily put the "function state" in the class.
May I present another alternative which might be a bit nicer to use and will look the same for both methods and functions:
If you like the usage, here's the implementation:
The problem is that class methods are descriptor objects, not functions. You can use the same decorator for both types of callables, in Python v2.6 on including v3.x, if you do a little more work in methods. Here's what I mean:
What the method version does is call the descriptor's
__get__
method to get a bound method instance object, and then accesses its__func__
attribute to get the actual function instance which has the named attribute attached to it.For versions of Python before 2.6, you would need to use
im_func
instead of__func__
.Update:
Most of the issues noted can be avoided by changing the decorator so that it adds an argument to the beginning of the call and writing the decorated functions to refer to that rather than to themselves to access the variables. Another nice thing is this approach works in both Python 2.x and 3.x:
This decorator allows adding more than one static:
You want to access the function object, but you are instead accessing a method. Python treats functions on instances and classes as descriptors, returning bound methods at lookup time.
Use:
to reach the wrapped function object.
In Python 3, you can also access the function object on the class:
In Python 2 that'd still return an unbound method object (a method without an instance attached).
Of course, just because you can do this, does not necessarily make it a good idea. With a method you have a class, giving you an alternative location to store that counter. You could put it on either
Counter
or ontype(self)
, where the latter would give you a counter per subclass.