如何为Python属性实现__iadd__

pto*_*ato 13 python properties descriptor magic-methods

我正在尝试创建一个Python属性,其中就地添加由不同的方法处理,而不是检索值,添加另一个值并重新分配.因此,对于x对象上的属性o,

o.x += 5
Run Code Online (Sandbox Code Playgroud)

应该不同于

o.x = o.x + 5
Run Code Online (Sandbox Code Playgroud)

最终的价值o.x应该是相同的,以免混淆人们的期望,但我想让就地增加更有效率.(实际上,操作比简单添加需要更多的时间.)

我的第一个想法是在课堂上定义

x = property(etc. etc.)
x.__iadd__ = my_iadd
Run Code Online (Sandbox Code Playgroud)

但这引发了一个AttributeError,大概是因为property工具__slots__

我的下一次尝试使用描述符对象:

class IAddProp(object):
    def __init__(self):
        self._val = 5

    def __get__(self, obj, type=None):
        return self._val

    def __set__(self, obj, value):
        self._val = value

    def __iadd__(self, value):
        print '__iadd__!'
        self._val += value
        return self

class TestObj(object):
    x = IAddProp()
    #x.__iadd__ = IAddProp.__iadd__  # doesn't help

>>> o = TestObj()
>>> print o.x
5
>>> o.x = 10
>>> print o.x
10
>>> o.x += 5  # '__iadd__!' not printed
>>> print o.x
15
Run Code Online (Sandbox Code Playgroud)

如您所见,__iadd__不会调用特殊方法.我无法理解为什么会这样,虽然我猜测对象__getattr__是以某种方式绕过它.

我怎样才能做到这一点?我没有得到描述符的意思吗?我需要一个元类吗?

eca*_*mur 7

__iadd__只会查找返回的值__get__.你需要使__get__(或属性getter)返回一个对象(或代理对象)__iadd__.

@property
def x(self):
    proxy = IProxy(self._x)
    proxy.parent = self
    return proxy

class IProxy(int, object):
    def __iadd__(self, val):
        self.parent.iadd_x(val)
        return self.parent.x
Run Code Online (Sandbox Code Playgroud)


Sve*_*ach 6

+=线路中的操作员

o.x += 5
Run Code Online (Sandbox Code Playgroud)

被翻译成

o.x = o.x.__iadd__(5)
Run Code Online (Sandbox Code Playgroud)

右侧的属性查找转换为

o.x = IAddProp.__get__(TestObj2.x, o, TestObj2).__iadd__(5)
Run Code Online (Sandbox Code Playgroud)

可以看到,__iadd__()是在属性查找的返回值上调用的,所以需要__iadd__()在返回的对象上实现。