我想在我的一个类中为实例方法添加一个属性。我尝试了this question中给出的答案,但这个答案只适用于函数 - 据我所知。
作为一个例子,我希望能够做到这样的事情:
class foo(object):
...
def bar(self):
self.bar.counter += 1
return self.bar.counter
bar.counter = 1
...
但是,当我调用foo()。bar()时,我得到:
AttributeError: 'instancemethod' object has no attribute 'counter'
我这样做的目的是试图强调'counter'变量是bar()方法的本地变量,并且还避免使用另一个属性混乱我的类名称空间。有没有办法做到这一点?有更多的pythonic方式吗?
答案 0 :(得分:8)
在Python 3中,您的代码可以正常工作,但在Python 2中,在查找方法时会发生一些包装。
类级别:使用函数存储counter
(直接或使用可变默认值)有效地使它成为类级属性,因为只有一个函数,无论有多少你拥有的实例(它们都共享相同的函数对象)。
实例级别:要使counter
成为实例级别属性,您必须在__init__
中创建函数,然后用functools.partial
包装它(因此它的行为类似于普通方法),然后将其存储在实例上 - 现在每个实例都有一个函数对象。
静态类变量的公认惯例是使用可变的默认参数:
class foo(object):
...
def bar(self, _counter=[0]):
_counter[0] += 1
return _counter[0]
如果你想要它更漂亮,你可以定义自己的可变容器:
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
并改变你的代码:
class foo(object):
def bar(self, _counter=MutableDefault()):
_counter += 1
return _counter.value
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)
正如您所看到的,当移动到counter
的实例级别时,可读性受到严重影响。我强烈建议您重新评估强调counter
是bar
的一部分的重要性,如果真的很重要,可能会使bar
自己的类,其实例成为{{{}的实例的一部分1}}。如果它不是很重要,那就按照正常的方式进行:
foo
答案 1 :(得分:2)
很抱歉找到一篇较旧的帖子,但我在搜索中发现了它并且实际上找到了一个有解决方案的人。
这是一篇博客文章,描述了您所遇到的问题以及如何创建一个能够满足您需求的装饰器。您不仅可以获得所需的功能,而且还可以很好地解释Python为何以这种方式工作。
http://metapython.blogspot.com/2010/11/python-instance-methods-how-are-they.html
答案 2 :(得分:0)
您需要__init__
方法来初始化数据成员:
class foo(object):
def __init__(self):
self.counter = 0
def bar(self):
self.counter += 1
return self.counter
将数据值直接附加到函数上绝对不是Pythonic。
答案 3 :(得分:0)
如果你希望计数器在所有实例中都是持久的,你可以执行以下操作,这仍然不是Pythonic。
class foo(object):
def bar(self):
self.bar.im_func.counter += 1
return self.bar.im_func.counter
bar.counter = 0
答案 4 :(得分:0)
您不能直接将属性添加到实例方法,但是实例方法是函数的包装器,您可以将属性添加到包装的函数。即
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
这有效,但是counter有效地变成了一个类变量。