Skip to main content
 首页 » 编程设计

Python 3 绑定(bind)方法订阅

2025年05月04日9zengkefu

一开始,我知道 Python 3 中不存在绑定(bind)方法属性(根据本主题:Why does setattr fail on a bound method)

我正在尝试编写一个伪“响应式(Reactive)”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 装饰器使方法(不是函数!)可绑定(bind),并能够稍后将其“绑定(bind)”到回调 - 这样,如果有人调用方法,回调将被自动调用。

在 Python 3 中有什么方法可以做到这一点吗?

请您参考如下方法:

是啊! :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 的描述符来存储当前实例指针。

因此,我们能够将回调绑定(bind)到任何 Python 函数。执行开销不是太大 - empty 函数执行比没有这个装饰器慢 5 - 6 倍。此开销是由所需的函数链和由事件处理引起的。

当使用“正确的”事件实现(使用弱引用)时,像这样:Signal slot implementation ,我们得到的开销是基本函数执行的 20 到 25 倍,这仍然很好。

编辑: 根据 Hyperboreus 问题,我更新了代码,以便能够从回调方法中读取调用回调的源对象。它们现在可以通过 event.source 变量访问。