6

ユーザー入力に応じてモジュールの変数セットを返す Web アプリケーションに取り組んでいます。各モジュールは、単一のパラメーターを受け入れ、出力を含む「.html」プロパティを持つコンストラクターを持つ Python クラスです。

グローバル名前空間からクラスを動的にプルすると、次のように機能します。

result = globals()[classname](param).html

そして、それは確かに以下よりも簡潔です:

if classname == 'Foo':
    result = Foo(param).html
elif classname == 'Bar':
    ...

これを文体的に書くための最良の方法と考えられるものは何ですか? グローバル名前空間を使用しないリスクや理由はありますか?

4

3 に答える 3

6

このアプローチの欠点は、ユーザーが望む以上のことを実行できるようになる可能性があることです。名前を指定するだけで、その名前空間内の任意の単一パラメーター関数を呼び出すことができます。いくつかのチェック(例:isinstance(SomeBaseClass、theClass))でこれを防ぐことができますが、このアプローチを避ける方がおそらく良いでしょう。もう1つの欠点は、クラスの配置が制約されることです。それらをモジュールにグループ化するために、ルックアップコードは機能しなくなります。

いくつかの代替オプションがあります。

  1. 明示的なマッピングを作成します。

     class_lookup = {'Class1' : Class1, ... }
     ...
     result = class_lookup[className](param).html
    

    ただし、これには、すべてのクラスを再リストする必要があるという欠点があります。

  2. クラスを囲んでいるスコープにネストします。例えば。それらを独自のモジュール内、または外部クラス内で定義します。

    class Namespace(object):
        class Class1(object):
            ...
        class Class2(object):
            ...
    ...
    result = getattr(Namespace, className)(param).html
    

    ただし、ここでいくつかの追加のクラス変数(__bases __、__ getattribute__など)を誤って公開します。おそらく悪用可能ではありませんが、完全ではありません。

  3. サブクラスツリーからルックアップ辞書を作成します。すべてのクラスを単一の基本クラスから継承します。すべてのクラスが作成されたら、すべての基本クラスを調べて、それらからdictを入力します。これには、クラスをどこにでも(たとえば、個別のモジュールで)定義できるという利点があり、すべてが作成された後にレジストリを作成する限り、それらを見つけることができます。

    def register_subclasses(base):
        d={}
        for cls in base.__subclasses__():
            d[cls.__name__] = cls
            d.update(register_subclasses(cls))
        return d
    
    class_lookup = register_subclasses(MyBaseClass)
    

    上記のより高度なバリエーションは、自己登録クラスを使用することです-作成されたクラスをdictに自動的に登録するよりもメタクラスを作成します。これはおそらくこの場合はやり過ぎですが、一部の「ユーザープラグイン」シナリオでは役立ちます。

于 2008-10-21T15:43:46.980 に答える
4

まず第一に、あなたは少し車輪の再発明をしているように思えます...ほとんどのPython Webフレームワーク(CherryPy / TurboGearsは私が知っているものです)には、URLの内容に基づいて特定のクラスにリクエストをディスパッチする方法がすでに含まれています、またはユーザー入力。

実際、あなたのやり方には何の問題もありませんが、私の経験では、それはあなたのプログラムにある種の「抽象化の欠如」を示している傾向があります。基本的に、Pythonインタープリターを使用して、自分で保存するのではなく、必要になる可能性のあるオブジェクトのリストを保存します。

したがって、最初のステップとして、呼び出したいすべてのクラスの辞書を作成することをお勧めします。

dispatch = {'Foo': Foo, 'Bar': Bar, 'Bizbaz': Bizbaz}

最初は、これはあまり違いはありません。ただし、Webアプリが大きくなると、いくつかの利点があります。(a)名前空間の衝突に遭遇しない、(b)使用globals()すると、セキュリティ上の問題が発生する可能性があり、攻撃者は、本質的に、プログラム内のグローバルシンボルにアクセスできます。彼らはあなたのプログラムに任意のものを注入する方法を見つけることができますclassname、(c)あなたがclassname実際の正確なクラス名以外のものになりたい場合は、あなた自身の辞書を使用する方がより柔軟になります、(d)あなたはdispatch辞書を必要に応じてデータベースアクセスなどを行う、より柔軟なユーザー定義クラス。

セキュリティの問題は、Webアプリで特に顕著です。Webフォームから入力されたglobals()[variable]場所を実行することは、問題を抱えているだけです。variable

于 2008-10-21T15:32:59.030 に答える
0

クラス名とクラスの間のマップを作成する別の方法:

クラスを定義するときは、ルックアップ テーブルに入れたい任意のクラスに属性を追加します。次に例を示します。

class Foo:
    lookup = True
    def __init__(self, params):
        # and so on

これが完了したら、ルックアップ マップを作成します。

class_lookup = zip([(c, globals()[c]) for c in dir() if hasattr(globals()[c], "lookup")])
于 2008-10-22T06:18:41.553 に答える