定义类时自动注册(但无需在任何地方导入)

Posted

技术标签:

【中文标题】定义类时自动注册(但无需在任何地方导入)【英文标题】:Auto register a class when it's defined (but without importing it anywhere) 【发布时间】:2011-11-07 22:50:23 【问题描述】:

我想在创建类(不是实例)时注册它...但不导入它。

基本上,我想做这里描述的事情:

How to auto register a class when it's defined

...但不必在任何地方导入已注册的类。

问题是:在我的应用程序的某个点,我需要运行某些系统命令(让我们坚持重启、停止...例如)。我创建了一个“基础”(抽象-mmnnno-not-really looking)类“Command”和一个“CommandManager”,它将接收一个带有要运行的命令的字符串,为所述命令创建适当的实例,将其排队并分派在需要时(或尽可能)发出命令。 “命令”类的所有实例都将覆盖实际执行命令的“执行”方法。如果我想“重启”,我会在“RebootCommand”类上扩展“Command”类,并覆盖那个“execute”,调用外部应用程序“shutdown -r”或“rebo​​ot”。

最终目标是能够做到以下几点:

CommandManager.execute("reboot")

系统将重新启动。

为此,“reboot”字符串必须在 CommandManager 内的字典中注册。该字典将以“reboot”(字符串)作为键,将类 RebootCommand 作为值。 CommandManager 将收到字符串“reboot”,将转到它的“commandDictionary”,创建 RebootCommand 类的实例并调用其 execute() 方法(从而重新启动系统)。

类似:

#------------- CommandManager.py -------------
@classmethod
def execute(cls, parameter):
    if parameter in cls.validCommands:
        tmpCommand = cls.validCommands[parameter]()
        tmpCommand.execute()
#---------------------------------------------

为了进行注册,必须创建 RebootCommand 类对象,为了做到这一点,我必须在某处导入它。但我不想。我不需要。我只需要创建 RebootCommand 类对象,因此它将为发生注册的 Command MetaClass 执行 __new__ 方法(如上述其他问题的答案中所述)。

有什么办法吗?我已经尝试在__init__.py 中导入实际的命令(从Command 继承的类),但是,如果我创建一个新命令,我必须将它添加到__init__.py,我想避免这种情况(所以另一个程序员可以创建一个新命令而不必担心忘记将类添加到__init__.py)

这是我的目录结构:

commands/
    __init__.py
    CommandManager.py
    Command.py
    RebootCommand.py

这里是进行注册的 Command 和 RebootCommand 的内容:

#--------------- Command.py -------------------
def register(newCommand):
    if newCommand and newCommand._command:
        import CommandManager
        CommandManager.CommandManager.registerCommand(newCommand._command, newCommand)


# Fancy registering! https://***.com/questions/5189232/how-to-auto-register-a-class-when-its-defined
class CommandMetaClass(type):
    def __new__(cls, clsname, bases, attrs):
        newCommand = super(cls, CommandMetaClass).__new__(cls, clsname, bases, attrs)
        register(newCommand)  # here is your register function
        return newCommand

class Command(object):
    __metaclass__ = CommandMetaClass
    _command = None
#-----------------------------------------------------

还有……

#--------------- RebootCommand.py -------------------
class RebootCommand(Command.Command):
    _command = "reboot"
#---------------------------------------------------

如果我打开 __init__.py 并写:

import RebootCommand

(此时RebootCommand类对象就创建好了,

CommandMetaClass.__new__

函数运行,“rebo​​ot”注册为RebootCommand类,大家开心)

但如果可能的话,我想避免这样做(触摸__init__.py

我尝试使用 * 通配符导入,但没有成功。

总而言之,我希望另一个程序员想要实现一个新的命令,只需要创建一个继承自命令的新类,但不必将它添加到__init__.py

我知道没有什么是不能用 Command.py 文件中的注释来解决的,比如“任何继承自此的类都必须添加到 __init__.py”,但我很想知道我是否可以用更“优雅”的方式来做。

【问题讨论】:

您知道可以使用__import__ 函数或模块imp 按需导入模块吗?如果您保留有关如何将类名转换为文件名的某些规则,则可以通过字符串动态加载模块。 【参考方案1】:

我觉得你的方法实现你想要的功能有点复杂。我的建议是你使用这样的策略:

    为您的模块命名,以便在以它们实现的命令命名的文件中定义命令类。例如,RebootCommand 类进入文件commands/reboot.py

    每个命令模块都定义了一个***变量command_cls,其中包含对实现该命令的类的引用。 commands/reboot.py 因此将包含如下代码:

    class RebootCommand:
      def execute(self):
        # [...]
    
    command_cls = RebootCommand
    

    要执行命令,您可以使用__import__ 函数来动态加载您的模块。您的CommandManager.py 逻辑上将存在于commands 目录(为命令模块保留)之外的文件中,并将按如下方式实现:

    class CommandManager:
      @classmethod
      def execute(cls, command, *args, **kw):
        # import the command module
        commands_mod = __import__("commands", globals(), locals(), [command])
        mod = getattr(commands_mod, command)
        return mod.command_cls(*args, **kw).execute()
    

编辑:似乎我混淆了imp 模块的用法。更新代码以使用__import__

【讨论】:

以上是关于定义类时自动注册(但无需在任何地方导入)的主要内容,如果未能解决你的问题,请参考以下文章

l4自动驾驶标准是啥?

Android:定义一次视图及其子项,并在任何地方重用它

创建一个 NSError 域

WooCommerce:在任何地方添加/显示产品或变体自定义字段

免费音乐播放器-airplay(网上收集,仅供学习与研究,支持正版)

django 在任何地方都提供相同的会话