4

Python 変数として動作するクラスを作成したいと思いますが、「変数」が変更/読み取りされたときにコールバック関数を呼び出します。

つまり、次のようにクラスを使用できるようにしたいと考えています。

x=myClass(change_callback, read_callback)

x を myclass のインスタンスとして定義します。コンストラクター ( INIT ) は、パラメーターとして 2 つの関数を受け取ります。x が変更されるたびに呼び出される関数と、x が「読み取られる」たびに呼び出される関数です。

次のステートメント:

x=1

1を「保存」し、change_callback(1)何でもできる呼び出しをトリガーします。

次のステートメント:

a=x

保存された値を取得し、保存された値を変更して他のことを行う可能性のある呼び出しread_callback()を行います。

たとえば、次のようなものを記述できるようにするために、これを任意のタイプで機能させたいと思います。

x=[1 2 3]トリガーするものchange_callback([1 2 3])

x.append(4)トリガーchange_callback([1 2 3 4])します(およびおそらくread_callback()before への呼び出し)

x={'a':1}トリガーしますchange_callback({'a':1})

print(x)read_callback() への呼び出しをトリガーします...もちろん、最後に保存された値を印刷用に返します。

アイデアは、変数へのアクセスをログに記録したり、他の計算を生成したりできるということです...一見。

これが可能であるべきだと感じていますが、オブジェクトが何から継承する必要があるのか​​ よくわかりません...リストなどの1つのタイプに制限する必要がある場合、すべての代入演算子を再定義する方法はありますかappend()...) のようなメソッドを「一度に」実行して、元の動作 (基本クラスのメソッド) を維持し、コールバックを追加します...

または、同じ目標を達成するためのより適切な方法 (モジュール...) はありますか?

前もって感謝します、

4

4 に答える 4

8

オブジェクトは、いつ変数に割り当てられるかを知りません。書き込みx = aにより、 を指す dict エントリ (または locals エントリ) が追加されます。オブジェクトは通知さません (ただし、CPython では、その参照カウントはインクリメントされます)。

通知を受ける部分は、オブジェクトが割り当てられているコンテナーです。グローバル割り当ての場合、モジュール ディクショナリが更新されます。のようなインスタンス変数の更新の場合a.b = x、ドット ルックアップがあり、インスタンス ディクショナリに保存されます。

これらのコンテナーは、ルックアップまたはストアで任意のコードを呼び出すことができます。Python のプロパティは、この機能を新しいスタイルのクラスに提供します。execおよびeval操作を使用すると、通常の割り当てにこの機能を提供できるカスタム dict を指定できます。どちらの場合も、割り当て時に何が起こるかを完全に制御できます。

概要: ルックアップとストアの動作は、割り当てられるオブジェクトではなく、割り当てのターゲットを通じて制御できます。

from collections import namedtuple

CallbackVar = namedtuple('CallbackVar', ['change_callback', 'read_callback'])

class CallbackDict(dict):
    'Variant of dict that does callbacks for instances of CallbackVar'
    def __getitem__(self, key):
        value = dict.__getitem__(self, key)
        if isinstance(value, CallbackVar):
            return value.read_callback(key)
    def __setitem__(self, key, value):
        try:
            realvalue = dict.__getitem__(self, key)
            if isinstance(realvalue, CallbackVar):
                return realvalue.change_callback(key, value)
        except KeyError:
            pass
        return dict.__setitem__(self, key, value)

stmts = '''
x = CallbackVar(setter, getter)     # Attach getter() and setter() to "x"
x = 1                               # Invoke the setter()
x                                   # Invoke the getter()
'''

def getter(key):
    print 'Getting', key
    return 42

def setter(key, value):
    print 'Setting', key, 'to', value

exec stmts in globals(), CallbackDict()
于 2012-03-24T17:53:47.097 に答える
4

記述子を見たいかもしれません: http://docs.python.org/howto/descriptor.html

ただし、これはオブジェクト プロパティに対してのみ機能しますが、おそらくこれで十分です。

于 2012-03-24T17:53:56.270 に答える
2

演算子を使用してこれを行うことはできません。=定義により、左側を上書きするように設計されているため、その現在の内容が何であれ、そのようなことが起こっているという通知を受け取りません。(また、代入演算子は Python ではオーバーライドできません。)

ただし、できることは、クラスで.set()andメソッドを使用することです。.get()まったく同じ「魔法」は得られませんが、正直なところ、それはおそらく良いことです。何が起こっているのかがより明確になります。

于 2012-03-24T17:49:51.030 に答える
1

変数は、python オブジェクトへの参照です。「クラスは変数のように振る舞う」というのは間違いです。あなたがするとき

x = [1, 2, 3]

変数は、古いオブジェクトが「変更」されたのではなく、新しいリスト オブジェクトへのx参照 (リダイレクト) になります。[1, 2, 3]参照が失われただけです。

あなたが望むかもしれないことをするために。クラスchange_callbackのメソッドを呼び出してみてください。__init__

read_callback呼ばれる。このようなことをしてください

class A(object):
    def __init__(self): 
        self._x = 3
    @property
    def x():
        read_callback() # called!
        return self._x
    ....
a = A()
print a.x

これがあなたなら何ですか。

于 2012-03-24T18:07:59.680 に答える