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

sbe*_*ell 10 python methods instance instance-methods

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

举个例子,我希望能够做到这样的事情:

class foo(object):
    ...
    def bar(self):
        self.bar.counter += 1
        return self.bar.counter
    bar.counter = 1
    ...
Run Code Online (Sandbox Code Playgroud)

但是,当我调用foo().bar()时,我得到:

AttributeError: 'instancemethod' object has no attribute 'counter'
Run Code Online (Sandbox Code Playgroud)

我这样做的目的是试图强调'counter'变量是bar()方法的本地变量,并且还避免使用另一个属性混乱我的类名称空间.有没有办法做到这一点?有更多的pythonic方式吗?

Eth*_*man 8

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

类与实例

  • 类级别:counter使用函数存储(直接或使用可变默认值)有效地使它成为类级别属性,因为只有一个函数,无论你有多少个实例(它们都共享相同的函数对象) ).

  • 实例级别:要创建counter一个实例级别属性,您必须创建该函数__init__,然后将其包装functools.partial(因此它的行为类似于普通方法),然后将其存储在实例上 - 现在每个实例都有一个函数对象.

班级

静态类变量的公认惯例是使用可变的默认参数:

class foo(object):
    ...
    def bar(self, _counter=[0]):
        _counter[0] += 1
        return _counter[0]
Run Code Online (Sandbox Code Playgroud)

如果你想让它更漂亮,你可以定义自己的可变容器:

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
Run Code Online (Sandbox Code Playgroud)

并像这样更改您的代码:

class foo(object):
    def bar(self, _counter=MutableDefault()):
        _counter += 1
        return _counter.value
Run Code Online (Sandbox Code Playgroud)

实例级别

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)
Run Code Online (Sandbox Code Playgroud)

摘要

正如您所看到的,可移植性在转移到实例级别时受到了严重影响counter.我强烈建议你重新评估强调这counter是其中一部分的重要性bar,如果真的很重要,可能会创建bar自己的类,其实例成为实例的一部分foo.如果它不是很重要,那么按照正常方式进行:

class foo(object):
    def __init__(self):
        self.bar_counter = 0
    def bar(self):
        self.bar_counter += 1
        return self.bar_counter
Run Code Online (Sandbox Code Playgroud)