我想在创建时注册一个类(不是实例)但不导入它。
基本上,我想做这里描述的内容:
How to auto register a class when it's defined
...但无需在任何地方导入已注册的课程。
问题出在这里:在我的应用程序的某个时刻,我需要运行某些系统命令(让我们坚持使用reboot,停止......例如)。我已经创建了一个“base”(抽象-mmnnno-not-really look)类“Command”和一个“CommandManager”,它将接收一个包含要运行的命令的字符串,为所述命令创建适当的实例,对其进行排队并调度需要时(或可能的话)命令。 “Command”类的所有实例都将覆盖实际执行命令的“execute”方法。如果我想“重启”,我会在“RebootCommand”类上扩展“Command”类,并覆盖“执行”,调用外部应用程序“shutdown -r”或“reboot”。
最终目标是能够做到这样的事情:
CommandManager.execute("reboot")
系统将重启。
为此,必须在CommandManager内的字典中注册“ reboot ”字符串。该字典将“ 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://stackoverflow.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__
函数运行,“reboot”注册为RebootCommand类,每个人都很高兴)
但是如果可能的话,我想避免这样做(触摸__init__.py
)
我尝试使用*通配符导入但没有运气。
总而言之,我希望另一个想要实现新命令的程序员,只需要创建一个继承自Command的新类,而不必将其添加到__init__.py
我知道用Command.py文件中的注释无法解决这个问题,说“必须在__init__.py
中添加任何继承此类的类”,但我很想知道如果我能以更“优雅”的方式做到这一点。
答案 0 :(得分:2)
我觉得你的方法实现你想要的功能有点复杂。我的建议是你使用这样的策略:
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__
。