gpt4 book ai didi

Python 3 绑定(bind)方法订阅

转载 作者:太空宇宙 更新时间:2023-11-04 06:33:10 26 4
gpt4 key购买 nike

一开始,我知道 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 变量访问。

关于Python 3 绑定(bind)方法订阅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14617659/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com