python - クラスが定義されたときに自動登録する(ただし、どこにもインポートしない)

原文 python oop class metaclass

作成時にクラス(インスタンスではない)を登録したいのですが...インポートしません。

基本的に、私はここで説明されていることを行いたいです:

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__を使用するようにコードを更新しました。
関連記事

python - Pylonsを初めて使用し、ルートを理解しようとする

python - Pythonでの配列のすべての要素の文字列操作

python - 文字列から日時変数形式

python - Pythonでユニコード文字のリストをヘブライ文字列に変換する

python - Python executorの作成に関する問題

python - 孫ではなく子供のCPU使用率を測定する

python - 極座標プロットのエラーバーがmatplotlibの角度で回転しません

python - ユーザー定義クラスに異なる名前を使用すると、2つのPythonスクリプトの動作が異なる

python - python +セキュリティ

python - エキスパンダーラベルのPangoマークアップ