我需要在我的谷歌应用程序引擎应用程序中的事件消息系统。
我指的是下面的python库。
http://pubsub.sourceforge.net/apidocs/concepts.html
我的问题是,我想要执行的侦听器函数是否必须导入(或存在)到执行路径中的某个位置,才能在事件上运行它?
有很多事件,我想让它尽可能地延迟加载。
还能做些什么呢?
python中有没有延迟事件发布订阅框架?
发布于 2010-07-13 14:45:00
tipfy ( App-Engine特定的微框架)有延迟加载,但仅用于特定的“事件”,即您的代码正在服务的web请求。其他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习惯用法的一个相当好的例子,一些讨厌的笨蛋大声疾呼地断言,这个习惯用法不存在,或者不应该存在,或者是动态地改变自己的类的对象。这个习惯用法的用例是为存在于两种状态之一的对象“充当中间人”,从第一种状态到第二种状态的转换是不可逆的。在这里,懒惰调用者的第一个状态是“我知道函数的名称,但没有对象”,第二个状态是“我知道函数对象”。因为从第一个到第二个是不可逆的,所以如果您愿意,可以减少每次测试的小开销(或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:
检查;但这是一个真正的收益,没有真正的缺点,除了导致以前被命名的任性愚蠢的人陷入他们典型的愤怒和盲目的疯狂(如果你认为这是一个缺点)。
无论如何,实现的选择取决于您,而不是我--或者,幸运的是,他们;-)。
这是一种设计选择:是像tipfy
那样直接修补register
本身以直接接受字符串参数(并根据需要对它们进行包装),还是在注册站点进行显式包装,让register
(或subscribe
或其他名称)保持原样。在这种特殊情况下,我并不认为“显式比隐式更好”这句话有多重要,因为像这样
register(somevent, 'package.module.function')
非常明确,就像
register(somevent, LazyCall('package.module.function'))
也就是说,it 非常清楚发生了什么,而且可以说它更干净/更具可读性。
不过,显式包装方法保持底层框架不变是非常好的:在任何可以传递函数的地方,现在都可以无缝地传递该函数的名称(作为命名包、模块和函数本身的字符串)。所以,如果我改造现有的框架,我会选择显式的方法。
最后,如果您希望注册不是函数但(例如)某些类的实例或此类实例的绑定方法的可调用对象,则可以为此将LazyCall
丰富为LazyInstantiateAndCall
&c等变体。当然,体系结构会变得稍微复杂一些(例如,因为您想要实例化新对象的方法和识别已有对象的方法),但是通过将该工作委托给设计良好的工厂系统,应该不会太糟糕。然而,我不会在这样的细化中更深入,因为这个答案已经相当长了,而且无论如何,在许多情况下,简单的“命名函数”方法应该就足够了!-)
https://stackoverflow.com/questions/3237859
复制相似问题