15

インプレース追加が値の取得、別の値の追加、再割り当てとは異なる方法で処理されるPythonプロパティを作成しようとしています。したがって、xオブジェクトのプロパティの場合o

o.x += 5

動作が異なるはずです

o.x = o.x + 5

人々の期待を混乱させないために、の値はo.x最終的に同じである必要がありますが、インプレース追加をより効率的にしたいと思います。(実際には、単純な加算よりも操作にかなりの時間がかかります。)

私の最初のアイデアは、クラスで、

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__オブジェクトがどういうわけかそれをバイパスしていると推測しますが、これがなぜであるかを理解するのに苦労しています。

これどうやってするの?記述子のポイントを取得していませんか?メタクラスが必要ですか?

4

4 に答える 4

8

__iadd__から返された値でのみ検索されます__get____get__を使用してオブジェクト(またはプロキシオブジェクト)を返すように(またはプロパティゲッターに)作成する必要があります__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
于 2012-08-16T13:20:20.320 に答える
6

行の+=演算子

o.x += 5

に翻訳されます

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

右側の属性ルックアップは次のように変換されます

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

ご覧のとおり、は属性ルックアップの戻り値__iadd__()で呼び出されるため、返されたオブジェクトに実装する必要があります。__iadd__()

于 2012-08-16T13:40:17.727 に答える
2

次の例のようなものはどうでしょうか。基本的には、Barクラスに、プロパティ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)

編集:プロパティとして使用する方法を示す拡張例。私は最初に問題を少し誤解しました。

于 2012-08-16T13:19:32.973 に答える
1

あなたにインスピレーションを与えるために、これは私がこれまでに思いついた中で最高の理想的とは言えない解決策です:

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することで、演算子も継承できないようです。

于 2012-08-16T13:18:44.487 に答える