3

最初は、バインドされたメソッドの属性がPython 3に存在しないことを知っています(このトピックによると:バインドされたメソッドでsetattrが失敗するのはなぜですか

疑似「リアクティブ」Pythonフレームワークを作成しようとしています。多分私は何かが足りないのかもしれませんし、多分、私がやろうとしていることはどういうわけか実行可能であるということです。コードを見てみましょう:

from collections import defaultdict

class Event:
    def __init__(self):
        self.funcs = []

    def bind(self, func):
        self.funcs.append(func)

    def __call__(self, *args, **kwargs):
        for func in self.funcs:
            func(*args, **kwargs)


def bindable(func):
    events = defaultdict(Event)
    def wrapper(self, *args, **kwargs):
        func(self, *args, **kwargs)
        # I'm doing it this way, because we need event PER class instance
        events[self]()

    def bind(func):
        # Is it possible to somehow implement this method "in proper way"?
        # to capture "self" somehow - it has to be implemented in other way than now,
        # because now it is simple function not connected to an instance.
        print ('TODO')

    wrapper.bind = bind

    return wrapper

class X:
    # this method should be bindable - you should be able to attach callback to it
    @bindable
    def test(self):
        print('test')

# sample usage:

def f():
    print('calling f')

a = X()
b = X()

# binding callback
a.test.bind(f)

a.test() # should call f
b.test() # should NOT call f

もちろんEvent、この例では、のようなすべてのクラスが簡略化されています。このコードを修正して機能させる方法はありますか?デコレータを使用してメソッド(関数ではありません!)をバインド可能にし、後でコールバックに「バインド」できるようにしたいだけですbindable。このようにして、誰かがメソッドを呼び出すと、コールバックが自動的に呼び出されます。 。

Python 3でそれを行う方法はありますか?

4

2 に答える 2

1

そうそう!:D 私は答えを見つけました - 少し気が狂っていますが、速く働いています。誰かがコメントまたはより良い解決策を持っている場合、私はそれを見ることに非常に興味があります. 次のコードは、メソッドと関数に対して機能します。

# ----- test classes -----    
class Event:
    def __init__(self):
        self.funcs = []

    def bind(self, func):
        self.funcs.append(func)

    def __call__(self, *args, **kwargs):
        message = type('EventMessage', (), kwargs)
        for func in self.funcs:
            func(message)

# ----- implementation -----

class BindFunction:
    def __init__(self, func):
        self.func = func
        self.event = Event()

    def __call__(self, *args, **kwargs):
        out = self.func(*args, **kwargs)
        self.event(source=None)
        return out

    def bind(self, func):
        self.event.bind(func)

class BindMethod(BindFunction):
    def __init__(self, instance, func):
        super().__init__(func)
        self.instance = instance

    def __call__(self, *args, **kwargs):
        out = self.func(self.instance, *args, **kwargs)
        self.event(source=self.instance)
        return out

class Descriptor(BindFunction):
    methods = {}

    def __get__(self, instance, owner):
        if not instance in Descriptor.methods:
            Descriptor.methods[instance] = BindMethod(instance, self.func)
        return Descriptor.methods[instance]

def bindable(func):
    return Descriptor(func)

# ----- usage -----
class list:
    def __init__(self, seq=()):
        self.__list = [el for el in seq]

    @bindable
    def append(self, p_object):
        self.__list.append(p_object)

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

@bindable
def x():
    print('calling x')

# ----- tests -----

def f (event):
    print('calling f')
    print('source type: %s' % type(event.source))

def g (event):
    print('calling g')
    print('source type: %s' % type(event.source))

a = list()
b = list()

a.append.bind(f)
b.append.bind(g)

a.append(5)
print(a)

b.append(6)
print(b)

print('----')

x.bind(f)
x()

そして出力:

calling f
source type: <class '__main__.list'>
[5]
calling g
source type: <class '__main__.list'>
[6]
----
calling x
calling f
source type: <class 'NoneType'>

トリックは、Python の記述子を使用して現在のインスタンス ポインターを格納することです。

その結果、コールバックを任意の Python 関数にバインドできます。実行のオーバーヘッドはそれほど大きくありません。の関数の実行は、このデコレーターがない場合よりも 5 ~ 6 倍遅くなります。このオーバーヘッドは、必要な関数チェーンイベント処理によって発生します。

「適切な」イベント実装 (弱参照を使用) を使用する場合、次のように:シグナル スロットの実装では、基本関数の実行の 20 ~ 25 倍のオーバーヘッドが発生しますが、それでも問題ありません。

EDIT:Hyperboreusの質問によると、コールバックが呼び出されたソースオブジェクトをコールバックメソッドから読み取れるようにコードを更新しました。それらはevent.source変数によってアクセスできるようになりました。

于 2013-01-31T03:57:59.480 に答える
0

正直なところ、あなたの質問に対する答えはありません。別の質問が返されます。

インスタンスにモンキーパッチを適用しても、意図した動作が作成されません。

#! /usr/bin/python3.2

import types

class X:
    def __init__ (self, name): self.name = name
    def test (self): print (self.name, 'test')

def f (self): print (self.name, '!!!')

a = X ('A')
b = X ('B')

b.test = types.MethodType (f, b) #"binding"

a.test ()
b.test ()
于 2013-01-31T03:54:52.293 に答える