定义类时自动注册(但无需在任何地方导入)
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”或“reboot”。
最终目标是能够做到以下几点:
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__
函数运行,“reboot”注册为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__
。
【讨论】:
以上是关于定义类时自动注册(但无需在任何地方导入)的主要内容,如果未能解决你的问题,请参考以下文章
WooCommerce:在任何地方添加/显示产品或变体自定义字段