Adding attributes to instance methods in Python
Sorry to dig up an older post but I came across it in my searches and actually found someone with a solution.
Here's a blog post that describes the issue you're having and how to create a decorator that does what you want. Not only will you get the functionality you need but the author does a great job of explaining why Python works this way.
http://metapython.blogspot.com/2010/11/python-instance-methods-how-are-they.html
You can't add an attribute directly to an instancemethod but and instance method is a wrapper around a function and you can add an attribute to the wrapped function. ie
class foo(object):
def bar(self):
# When invoked bar is an instancemethod.
self.bar.__func__.counter = self.bar.__func__.counter + 1
return self.bar.__func__.counter
bar.counter = 1 # At this point bar is just a function
This works but counter effectively becomes a class variable.
In Python 3 your code would work, but in Python 2 there is some wrapping that takes place when methods are looked up.
Class vs Instance
class level: storing
counter
with the function (either directly, or by using a mutable default) effectively makes it a class level attribute as there is only ever one of the function, no matter how many instances you have (they all share the same function object).instance level: to make
counter
an instance level attribute you have to create the function in__init__
, then wrap it withfunctools.partial
(so it behaves like a normal method), and then store it on the instance -- now you have one function object for every instance.
Class Level
The accepted practice for a static-like variable is to use a mutable default argument:
class foo(object):
...
def bar(self, _counter=[0]):
_counter[0] += 1
return _counter[0]
If you want it to be prettier you can define your own mutable container:
class MutableDefault(object):
def __init__(self, start=0):
self.value = start
def __iadd__(self, other):
self.value += other
return self
def value(self):
return self.value
and change your code like so:
class foo(object):
def bar(self, _counter=MutableDefault()):
_counter += 1
return _counter.value
Instance level
from functools import partial
class foo(object):
def __init__(self):
def bar(self, _counter=MutableDefault(1)): # create new 'bar' each time
value = _counter.value
_counter += 1
return value
self.bar = partial(bar, self)
Summary
As you can see, readability took a serious hit when moving to instance level for counter
. I strongly suggest you reevaluate the importance of emphasizing that counter
is part of bar
, and if it is truly important maybe making bar
its own class whose instances become part of the instances of foo
. If it's not really important, do it the normal way:
class foo(object):
def __init__(self):
self.bar_counter = 0
def bar(self):
self.bar_counter += 1
return self.bar_counter