作成時にクラス(インスタンスではない)を登録したいのですが...インポートしません。
基本的に、私はここで説明されていることを行いたいです:
How to auto register a class when it's defined...しかし、登録されたクラスをどこにもインポートする必要はありません。
問題は次のとおりです。アプリケーションの特定の時点で、特定のシステムコマンドを実行する必要があります(例として、再起動、停止などを続けましょう)。私は「ベース」(abstract-mmnnno-not-really looking)クラス「Command」と「実行するコマンドの文字列を受け取り、そのコマンドの適切なインスタンスを作成し、キューに入れてディスパッチする「CommandManager」を作成しました。必要に応じて(または可能な場合はいつでも)コマンド。 「Command」クラスのすべてのインスタンスは、実際にコマンドを実行する「execute」メソッドを上書きします。 「再起動」したい場合は、「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__
メソッドが実行されます(上記の他の質問への回答で詳しく説明されています)。
それを行う方法はありますか?実際のコマンド(Commandから継承するクラス)を
__init__.py
にインポートしようとしましたが、新しいコマンドを作成する場合は、
__init__.py
に追加する必要があり、それを避けたい(そのため、別のプログラマーは、クラスを
__init__.py
に追加し忘れることを心配することなく、新しいコマンドを作成できます。
これが私のディレクトリ構造です:
commands/
__init__.py
CommandManager.py
Command.py
RebootCommand.py
そしてここでは、登録を行うコマンドと再起動コマンドの内容:
#--------------- 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
をタッチ)。
私は運が悪いのに*ワイルドカードを使ってインポートしようとしました。
要約すると、新しいコマンドを実装したい別のプログラマが、コマンドから継承する新しいクラスを作成するだけで、
__init__.py
に追加する必要がないことを望みます。
Command.pyファイルのコメントで「これから継承するクラスは
__init__.py
に追加する必要があります」のように言って修正できないものはないことはわかっていますが、できるかどうか知りたいです。より「エレガント」な方法で。
必要な機能を実装するためのアプローチは少し複雑だと思います。私の提案は次のような戦略を使用することです:
モジュールに名前を付け、コマンドクラスが、それらが実装するコマンドにちなんで名付けられたファイルで定義されるようにします。たとえば、
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__
を使用するようにコードを更新しました。