我需要在谷歌应用引擎应用程序中使用事件消息系统。
我指的是以下python库。
http://pubsub.sourceforge.net/apidocs/concepts.html
我的问题是,是否必须将我想要执行的侦听器函数导入(或以其他方式存储)到执行路径的某个位置以便在事件上运行它?
有很多事件,我希望尽可能延迟加载。
可能是什么工作?
在python中是否有任何惰性事件发布订阅框架?
答案 0 :(得分:1)
tipfy(特定于App-Engine的微框架)具有延迟加载,但仅适用于您的代码所服务的Web请求的特定“事件”。其他网络框架也有它,但是tipfy很小而且很简单,可以轻松地研究和模仿它的来源。
因此,如果由于“延迟加载”问题而无法找到更符合您口味的更丰富的事件框架,您可以选择一个需要注册/订阅可调用对象的框架,并允许字符串命名函数也要注册,就像tipfy一样。当然,如果需要为某个事件提供服务,那么这个命名的函数将被及时加载。
让我举一些简化的假设代码。假设您有一个包含以下内容的事件框架:
import collections
servers = collections.defaultdict(list)
def register(eventname, callable):
servers[eventname].append(callable)
def raise(eventname, *a, **k):
for s in servers.get(eventname, ()):
s(*a, **k)
当然,任何现实世界的事件框架的内部都会更加丰富,但是这样的东西在它的最底层都是可以辨别的。
因此,这需要在注册时加载callable ......然而,即使不触及框架的内部,也可以轻松扩展它。考虑:
import sys
class LazyCall(object):
def __init__(self, name):
self.name = name
self.f = None
def __call__(self, *a, **k):
if self.f is None:
modname, funname = self.name.rsplit('.', 1)
if modname not in sys.modules:
__import__(modname)
self.f = getattr(sys.modules[modname], funname)
self.f(*a, **k)
当然,你需要更好的错误处理& c,但这是它的要点:将命名函数的字符串(例如'package.module.func'
)包装到一个知道如何懒惰地加载它的包装器对象中。现在,register(LazyCall('package.module.func'))
将在未触及的框架中注册这样一个包装器 - 并根据请求延迟加载它。
这个用例,顺便说一句,可以作为Python习语的一个相当好的例子,一些愚蠢的傻瓜声称,大声和str,不存在,或不应该存在,或者某种东西:一个动态改变它的对象自己的班级。这个成语的用例是为两个状态之一中存在的对象“切割中间人”,从第一个到第二个的转换是不可逆转的。这里,懒惰调用者的第一个状态是“我知道函数的名称,但没有对象”,第二个是“我知道函数对象”。由于从第一个到第二个的移动是不可逆转的,如果你愿意,你可以减少每次测试的小开销(或Strategy
设计模式的间接开销):
class _JustCallIt(object):
def __call__(self, *a, **k):
self.f(*a, **k)
class LazyCall(object):
def __init__(self, name):
self.name = name
self.f = None
def __call__(self, *a, **k):
modname, funname = self.name.rsplit('.', 1)
if modname not in sys.modules:
__import__(modname)
self.f = getattr(sys.modules[modname], funname)
self.__class__ = _JustCallIt
self.f(*a, **k)
这里的收益是适度的,因为它基本上只是从每次通话中削减一个if self.f is None:
支票;但这是一个真正的收获,没有真正的缺点,除了导致先前命名的顽固的傻瓜fla到他们典型的愤怒和无意识的疯狂(如果你把 作为一个缺点)。
无论如何,实施选择取决于你,而不是我 - 或者,幸运的是,他们; - )。
一个设计选择:是否修补register
本身直接接受字符串参数(并根据需要包装它们),基本上就像tipfy
那样,或者在注册站点进行显式包装,离开register
(或subscribe
或者它被称为)原始。在这个特殊情况下,我没有通过“明确比隐含更好”的口头禅设定更多的权重,因为像
register(somevent, 'package.module.function')
与
一样明确register(somevent, LazyCall('package.module.function'))
,即 非常清楚发生了什么,它可以说更清晰/更具可读性。
然而, 真的很好,显式包装方法不会触及底层框架:无论你在哪里传递函数,你现在都可以传递该函数的名称 (作为字符串命名包,模块和函数本身),无缝地。那么,如果我改造现有的框架,我会采用明确的方法。
最后,如果您想要注册不是函数的callables(例如)某些类的实例,或者这些实例的绑定方法,您可以将LazyCall
丰富到LazyInstantiateAndCall
&等变体中; c为此目的。当然,架构变得有点复杂(因为你想要实例化新对象和识别已经存在的对象的方法),但是通过将这些工作委托给设计良好的工厂系统,它不应该太坏。但是,我没有深入研究这样的改进,因为这个答案已经相当长了,无论如何,在许多情况下,简单的“命名函数”方法就足够了! - )