在Python中向实例方法添加属性

时间:2012-03-01 20:18:00

标签: python methods instance instance-methods

我想在我的一个类中为实例方法添加一个属性。我尝试了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方式吗?

5 个答案:

答案 0 :(得分:8)

在Python 3中,您的代码可以正常工作,但在Python 2中,在查找方法时会发生一些包装。

Class vs Instance

  • 类级别:使用函数存储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的实例级别时,可读性受到严重影响。我强烈建议您重新评估强调counterbar的一部分的重要性,如果真的很重要,可能会使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有效地变成了一个类变量。