这是一个有趣的小问题,因为从表面上看,这似乎应该可以正常工作。但是,如果您在 Sublime 插件系统的内部进行了足够多的跟踪,那么其原因就会变得更加明显。
出于此答案的目的,我正在使用与您问题中的非工作示例匹配的插件,我将其命名为 test_plugin.py:
import sublime
import sublime_plugin
class test:
def on_modified(self):
print("mod")
class test2(test, sublime_plugin.ViewEventListener):
pass
首先,Sublime 使用 EventListener 和 ViewEventListener 的方式在插件方面有不同的内部机制。
EventListener 事件同样适用于任何地方的所有视图,因此当 Sublime 加载插件并找到 EventListener 类时,它会立即创建它的一个实例,然后检查该实例支持的事件。与此相关的代码在第 142 行附近的 reload_plugin() 方法中的 sublime_plugin.py 中:
if issubclass(t, EventListener):
obj = t()
for p in all_callbacks.items():
if p[0] in dir(obj):
p[1].append(obj)
all_callbacks 是一个字典,其中键是事件的名称,值是数组;因此,这将检查事件侦听器实例的dir() 是否包含事件,如果是,则将其添加到支持该事件的类列表中。
另一方面,ViewEventListener 仅适用于基于is_applicable 和applies_to_primary_view_only 类方法的某些视图。这意味着它必须存储 class 而不是 instance,以便在创建新视图时,它可以创建特定于该视图的实例。
相关代码就在上面,在sublime_plugin.py文件的第156行:
if issubclass(t, ViewEventListener):
view_event_listener_classes.append(t)
module_view_event_listener_classes.append(t)
现在让我们看看需要引发事件时会发生什么。在我们的示例中,我们正在查看 on_modified 事件,该事件由第 566 行的 sublime_plugin.py 中的 on_modified 模块函数处理(但所有事件的工作方式相似):
def on_modified(view_id):
v = sublime.View(view_id)
for callback in all_callbacks['on_modified']:
run_callback('on_modified', callback, lambda: callback.on_modified(v))
run_view_listener_callback(v, 'on_modified')
第一部分是普通的EventListener;它找到之前找到的所有具有on_modified 处理程序的对象实例,然后直接在它们上调用on_modified()(run_callback() 包装器负责为您在Tools > Developer > Profile Plugins 中看到的分析计时执行时间)。
ViewEventListener 的处理发生在 run_view_listener_callback() 中,在 sublime_plugin.py 的第 480 行附近:
def run_view_listener_callback(view, name):
for vel in event_listeners_for_view(view):
if name in vel.__class__.__dict__:
run_callback(name, vel, lambda: vel.__class__.__dict__[name](vel))
如果您已经像示例中那样定义了类,那么这就是事情开始变成梨形的部分,因为它专门询问 __dict__ 属性是否包含要调用的事件,并且仅在它调用它时才调用它可以。
根据上面的示例插件,请注意 Sublime 控制台中的以下内容:
>>> from User.test_plugin import test, test2
>>> "on_modified" in test.__dict__
True
>>> "on_modified" in test2.__dict__
False
特别是test2 不直接包含on_modified 方法,所以它不在__dict__ 中。在常规 Python 程序中,如果您尝试调用 on_modified,它会注意到它不在对象的 __dict__ 中,然后开始搜索层次结构,在 test 中找到它,一切都是肉汁。
在EventListener 实例的情况下,会发生这种情况; dir() 函数知道超类之一包含 on_modified 并返回它,调用它的调用沿着链向上,一切都按您的预期工作。
由于对run_view_listener_callback 的调用不是直接尝试调用该方法,它在直接__dict__ 查找中找不到它,因此什么也不做,因为它认为该类甚至不处理该事件虽然确实如此。
所以所有这堵文字墙的结果是,对于ViewEventListener,事件必须直接存在于继承ViewEventListener 的类中,否则它不起作用。
在您的示例中,一种方法是重构您的代码并直接在test2 中而不是在test 中定义这些方法。
另一种方法是通过在子类中定义方法来“代理”它们,但让主体遵循超类版本。这会将适当的方法放在类的 __dict__ 中,但仍然让实现出现在另一个类中。
class test2(test, sublime_plugin.ViewEventListener):
def on_modified(self):
super().on_modified()
我不是 Python 专家,但对我来说,这似乎并不过分 Pythonic(至少对于所展示的示例而言),并且可能还有其他方法可以实现相同的目标。