9

コンテクスト

私は自分のコードにいくつかの「プラグイン」(これがこれの正しい定義であるかどうかはわかりません)を持たせようとしています。「プラグイン」とは、モデル(これは科学的なコードです)を、その存在がコード内の他の場所で使用するのに十分な方法で定義するモジュールを意味します。

もちろん、これらのプラグインは、私のコードで定義されたいくつかのモジュール/関数/クラスを使用するテンプレートに従う必要があります。これが私のコードの関連部分の小さなスニペットです:

# [In the code]
class AllModels():
    def __init__(self):
        """
        Init.
        """
        self.count = 0

    def register(self, name, model):
        """
        Adds a model to the code
        """
        setattr(self, name, model)
        self.count += 1
        return

class Model():
    def __init__(self, **kwargs):
        """
        Some constants that defines a model
        """
        self.a = kwargs.get("a", None)
        self.b = kwargs.get("b", None)
        # and so on...

    def function1(self, *args, **kwargs):
        """
        A function that all models will have, but which needs:
            - to have a default behavior (when the instance is created)
            - to be redefinable by the "plugin" (ie. the model)
        """
        # default code for the default behavior
        return

instance = AllModels()

そしてここに「プラグイン」の関連部分があります:

# [in the plugin file]
from code import Model, instance
newmodel = Model(a="a name", b="some other stuff")

def function1(*args, **kwargs):
    """
    Work to do by this model
    """
    # some specific model-dependent work
    return

instance.register(newmodel)

追加情報と要件

  • function1どのモデルプラグインでもまったく同じシグネチャを持っていますが、通常はそれぞれに対して異なるジョブを実行しています。

  • プラグインで定義されていない場合でも、何かを実行できるように、デフォルトの動作がfunction1必要です(さまざまな可能性を試したり、警告/エラーを発生させたりします)。

  • プラグインでfunction1は、このプラグインでのみ定義されている他のいくつかの関数を使用できます。コードがマルチプロセッシングモジュールで実行されているため、これを述べています。子プロセスを呼び出すことができるようにするには、のinstanceインスタンスが必要です。は親プロセスとモデルプラグインで定義されますが、さまざまな子プロセスで使用されます(ただし、変更は行われません)。AllModelsfunction1instance

  • function1プラグインによって「再定義」されたときに、インスタンスの属性にアクセスできるのは素晴らしいことですModel(つまりself)。

問題

私はPythonドキュメントのさまざまなソースといくつかのSOの質問を読みました。私はこの問題に対して2つまたは3つの可能な解決策しか見ていません:

1)クラスでfunction1メソッドを宣言するのではなく、プラグインが新しいインスタンスを作成するときに属性として設定するだけです。Model

# [in the plugin file]
def function1(*args, **kwargs):
    # ....
    return
newmodel.function1 = function1

そして、必要なときにいつでもそれを呼び出します。その場合、function1オブジェクトの属性はおそらくModel開始されます。Noneその1つの注意点は、の「デフォルトの動作」がないことですfunction1(たとえば、テストなどのコードで処理する必要がありますif instance.function1 is None: ...)。さらに大きな問題は、この方法でアクセスできないことselfです...

2)どういうわけかPythonデコレータを使用します。私はこれを使用したことがなく、読んだドキュメントはそれほど単純ではありません(使用法の可能性が非常に多いため、単純ではありません)。しかし、それは良い解決策のようです。ただし、パフォーマンスへの影響が心配です(装飾された関数/メソッドの実行が遅くなる可能性があることを読みました)。このソリューションが最良のオプションである場合、それを使用する方法(おそらく簡単なスニペット)と、クラスの属性を使用できるかどうかを知りたいですModel

# [in the plugin file]
@mydecorator
def function1(self, *args, **kwargs):
    """
    I'm not sure I can use *self*, but it would be great since some attributes of self are used for some other function similar to *function1*...
    """
    # some stuff using *self*, eg.:
    x = self.var **2 + 3.4
    # where self.var has been defined before, eg.: newmodel.var = 100.

3)モジュールtypesとそのモジュールを使用するMethodType...私の場合、それが適切かどうかはわかりません...しかし、私は間違っている可能性があります。

この長い質問の後でわかるように、私はそのようなPython機能にあまり精通しておらず、デコレータについての私の理解は今では本当に貧弱です。いくつかのドキュメントを読み続けている間、問題を処理するための方向性がわからないので、ここで質問する価値があるかもしれないと思いました。

解決

Senderleの答えの美しさは、それが本当に単純で明白であるということです...そしてそれを逃したのは残念です。その質問でSOを汚染してすみません。

4

3 に答える 3

7

まあ、私が間違っていない限り、あなたはサブクラス Modelにしたいです。これは、のインスタンスを作成し、その属性をプラグインモジュールで定義された関数(つまり、オプション1)にModel置き換えるようなものです。function1しかし、それははるかにクリーンであり、あなたのためにすべての詳細を処理します:

# [in the plugin file]
from code import Model, instance

class MyModel(Model):
    def function1(*args, **kwargs):
        """
        Work to do by this model
        """
        # some specific model-dependent work
        return

newmodel = MyModel(a="a name", b="some other stuff")
instance.register(newmodel)

このようにして、他のすべてのメソッド(インスタンスに「アタッチ」された関数)は;Modelから継承されます。Modelそれらはまったく同じように動作しますが、function1オーバーライドされ、カスタマイズされfunction1た定義に従います。

于 2012-03-23T04:31:07.420 に答える
2

クラスと?にダミーfunction1()関数を書いていただけませんか?そうすれば、誰かが実装せずにから継承しようとすると、コードを実行しようとしたときに例外が発生します。それらのコードを実行している場合は、そのエラーをキャッチして、有用なエラーメッセージをユーザーに返すことができます。ModelraiseNotImplementedErrorModelfunction1()

例えば:

class Model:
    #Your code

    def function1():
        raise NotImplementedError("You need to implement function1 
            when you inherit from Model")

次に、コードを実行するときに次のことを実行できます。

try:
    modelObj.function1()
except NotImplementedError as e:
    #Perform error handling here

編集: NotImplementedErrorの公式Pythonドキュメントには、「ユーザー定義の基本クラスでは、メソッドをオーバーライドするために派生クラスが必要な場合、抽象メソッドでこの例外が発生する必要があります」と記載されています。これは、ここでの要件に適合しているようです。

于 2012-03-23T04:30:33.387 に答える
2

あなたがやろうとしていることは、非常に簡単な方法で行うことができます-オブジェクト指向の手法を使用し、Python関数でも通常のオブジェクトであることを利用するだけです-

簡単なことの1つは、「model」クラスに「function1」をパラメーターとして受け入れ、それをオブジェクトメンバーとして格納することです。

このようなコードの中には、コードへの変更を最小限に抑えたものもありますが、もっと興味深いことが確かに可能です。

# [In the code]

class AllModels():
    def __init__(self):
        """
        Init.
        """
        self.count = 0

    def register(self, name, **kwargs):
        """
        Adds a model to the code
        """
        model = Model(**kwargs)
        setattr(self, name, model)
        self.count += 1
        return

class Model():
    def __init__(self, **kwargs):
        """
        Some constants that defines a model
        """
        self.a = kwargs.get("a", None)
        self.b = kwargs.get("b", None)
        if "function1" in kwargs:
            self.real_function1 = kwargs["function1"]
            self.function1.__doc__ = kwargs["function1"].__doc__
        # and so on...

    def function1(self, *args, **kwargs):
        """
        A function that all models will have, but which needs:
            - to have a default behavior (when the instance is created)
            - to be redefinable by the "plugin" (ie. the model)
        """
        if self.real_function1:
            return self.real_function1(self, *args, **kwargs)
        # default code for the default behavior
        return

instance = AllModels()

# [in the plugin file]
from code import Model, instance
newmodel = Model(a="a name", b="some other stuff")

def function1(self, *args, **kwargs):
    """
    Work to do by this model
    """
    # some specific model-dependent work
    return


instance.register("name", function1 = function1, a="a name", b="some other stuff")
于 2012-03-23T04:37:20.283 に答える