如何实现__iadd__为一个Python属性(How to implement __iadd__

2019-06-27 02:17发布

我想创造一个就地加入由不同于检索值不同的方法来处理Python的性能,增加另一个值和重新分配。 因此,对于财产x上的对象o

o.x += 5

应该工作不同于

o.x = o.x + 5

ox应该是在年底相同,以免混淆人们的预期,但我想使就地添加更有效。 (在现实中,操作时间比简单的加法更多的时间。)

我的第一个想法是定义在类,

x = property(etc. etc.)
x.__iadd__ = my_iadd

但是,这引发了一个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

正如你所看到的,特别__iadd__方法不叫。 我无法理解这是为什么,但我猜测,该对象的__getattr__以某种方式绕过它。

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

Answer 1:

__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


Answer 2:

+=在生产线操作员

o.x += 5

被翻译成

o.x = o.x.__iadd__(5)

在右侧的属性查找被翻译成

o.x = IAddProp.__get__(TestObj2.x, o, TestObj2).__iadd__(5)

正如你所看到的, __iadd__()被调用的属性查找的返回值 ,所以你需要实现__iadd__()返回的对象上。



Answer 3:

为了激励你,这里有一个低于理想这是最好的我已经成功地拿出这么远的解决方案:

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

    def __str__(self):
        return str(self._val)

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

class TestObj2(object):
    def __init__(self):
        self._x = IAddObj()

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x._val = value

>>> o = TestObj2()
>>> print o.x
5
>>> o.x = 10
>>> print o.x
10
>>> o.x += 5
__iadd__!
>>> print o.x
15
>>> print o.x + 5  # doesn't work unless __add__ is also implemented
TypeError: unsupported operand type(s) for +: 'IAddObj' and 'int'

最大的缺点是,你必须执行的对数值魔术方法充分补充IAddObj如果您希望属性表现得像一个号码东西。 有IAddObj继承int似乎并没有让它继承的运营商,无论是。



Answer 4:

为什么不能像下面的例子。 基本想法是让律师类保证存储的值属性x始终是一个Foo对象。

class Foo(object):
    def __init__(self, val=0):
        print 'init'
        self._val = val

    def __add__(self, x):
        print 'add'
        return Foo(self._val + x)

    def __iadd__(self, x):
        print 'iadd'
        self._val += x
        return self

    def __unicode__(self):
        return unicode(self._val)

class Bar(object):
    def __init__(self):
        self._x = Foo()

    def getx(self):
        print 'getx'
        return self._x

    def setx(self, val):
        if not isinstance(val, Foo):
            val = Foo(val)
        print 'setx'
        self._x = val

    x = property(getx, setx)

obj = Bar()
print unicode(obj.x)
obj.x += 5
obj.x = obj.x + 6
print unicode(obj.x)

编辑:扩展的例子来说明如何使用它作为一个属性。 我第一次略有误解的问题。



文章来源: How to implement __iadd__ for a Python property