一开始,我知道Python 3中不存在绑定方法属性(根据本主题:Why does setattr fail on a bound method)
我正在尝试编写一个伪'反应'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中有没有办法做到这一点?
答案 0 :(得分: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 ()
答案 1 :(得分:0)
# ----- 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倍。这种开销是由事件处理所需的功能链和引起的。
当使用“正确”事件实现(使用弱引用)时,如下所示:Signal slot implementation,我们得到的开销是基本函数执行的20 - 25倍,这仍然是好的。
编辑:
根据Hyperboreus的问题,我更新了代码,以便能够从回调方法中读取调用回调的源对象。现在可以通过event.source
变量访问它们。