10

我想在我的一个类中为实例方法添加一个属性。我尝试了这个问题中给出的答案,但这个答案只适用于函数——据我所知。

例如,我希望能够执行以下操作:

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() 方法的本地变量,并且还避免将我的类命名空间与另一个属性混淆。有没有办法做到这一点?有没有更蟒蛇的方式来做到这一点?

4

5 回答 5

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是. 如果它不是很重要,请按正常方式进行:barbarfoo

class foo(object):
    def __init__(self):
        self.bar_counter = 0
    def bar(self):
        self.bar_counter += 1
        return self.bar_counter
于 2012-03-01T20:52:56.627 回答
2

很抱歉挖掘了一篇较旧的帖子,但我在搜索中遇到了它,实际上找到了一个有解决方案的人。

这是一篇博客文章,描述了您遇到的问题以及如何创建一个可以满足您需求的装饰器。你不仅会得到你需要的功能,而且作者也很好地解释了为什么 Python 会这样工作。

http://metapython.blogspot.com/2010/11/python-instance-methods-how-are-they.html

于 2012-12-26T17:57:10.493 回答
1

您不能将属性直接添加到实例方法,但实例方法是函数的包装器,您可以将属性添加到包装的函数。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

这有效,但 counter 有效地成为一个类变量。

于 2019-05-14T08:42:40.877 回答
0

您需要一个__init__方法来初始化数据成员:

class foo(object):
    def __init__(self):
        self.counter = 0
    def bar(self):
        self.counter += 1
        return self.counter

将数据值直接附加到函数显然不是 Pythonic。

于 2012-03-01T20:19:43.673 回答
0

如果您希望计数器在所有实例中保持不变,您可以执行以下操作,这仍然不是 Pythonic。

class foo(object):
    def bar(self):
        self.bar.im_func.counter += 1
        return self.bar.im_func.counter
    bar.counter = 0
于 2012-03-01T20:35:30.250 回答