1

重複の可能性:
カスタム名を使用した Python の動的関数の作成

私がやりたいことが可能かどうかを判断するための小さなスクリプトを書きました。です。

私の目標は、任意のサイズのリストに基づいた名前を持つ関数 (またはメソッド) を動的に (実行時に) 作成することです (リストのサイズ = 動的に作成される関数の数)。すべての関数は (今のところ) 同じことを行い、引数を出力するだけです。

次のコードはまさに私が望むことを行いますが、クリーンではなく、非常にブルートフォースです。これを行うためのより良い方法があるかどうかを理解しようとしています。

class Binder:
    def __init__(self, test_cases):
        """"
        test_cases: a list of function/method names.
        length of test_case = number of methods created.
        """

        for test_case in test_cases:
            #construct a code string for creating a new method using "exec" 
            func_str = "def "
            func_str += test_case
            func_str += "(*args):"
            func_str += "\n\t"
            func_str += "for arg in args:"
            func_str += "\n\t\t"
            func_str += "print arg"
            func_str += "\n"

            """
            For example, func_str for test_cases[0]= "func1" is simply:
            def func1(*args):
                for arg in args:
                    print arg
            """
            #use exec to define the function
            exec(func_str)

            #add the function as a method to this class
            # for test_cases[0] = "func1", this is: self.func1 = func1
            set_self = "self." + test_case + " = " + test_case
            exec(set_self)

if __name__ == '__main__':
    #this list holds the names of the new functions to be created
    test_cases = ["func1", "func2", "func3", "func4"]
    b = Binder(test_cases)

    #simply call each function as the instant's attributes
    b.func1(1)
    b.func2(1, 3, 5)
    b.func4(10)

出力は次のとおりです。

1
1
3
5
10

予想通り。

関数のコンテンツを更新することは、単に引数を出力する for ループではなく、より意味のあることを行います。上記のコードから、必要な正確な結果が得られますが、それを行うためのより良い方法があるかどうか疑問に思っています。

更新私ははるかに大きなモジュールの両端を結んでいます。一方の端では、テスト ケースが何であるかを判断し、とりわけ、テスト ケースの名前のリストを設定します。もう一方の端は関数自体であり、テスト ケースの名前と 1 対 1 でマッピングする必要があります。したがって、テスト ケースの名前がわかり、各テスト ケースで何をしたいのかがわかります。必要なのは、テスト ケースの名前を持つ関数を作成することだけです。テスト ケースの名前は実行時に決定されるため、それらのテスト ケースに基づく関数の作成も実行時に行う必要があります。テストケースの数も実行時に決定されます。

これを行うより良い方法はありますか?? あらゆる提案を歓迎します。

前もって感謝します。

マフディ

4

2 に答える 2

1

Python では、これが一般的なメタプログラミングの最も合理的なアプローチです。

ただし、コードにいくつかの定数だけが必要な場合は、クロージャがうまくいく可能性があります...たとえば:

def multiplier(k):
    "Returns a function that multiplies the argument by k"
    def f(x):
        return x*k
    return f

任意のコード生成のために、Python にはastモジュールがあり、理論的には、そのアプローチを使用して関数を検査または作成できます。ただし、Python コードでは、そのように表現して操作するのが難しいため、通常、アプローチは特定の関数をコンパイルするのではなく、実行時にすべてを行うことです。本当にアドバンテージを得ることができる場合は、eval(ラムダを取得するために) または を使用できますexec。ast モジュールは、主に検査のみに使用されます。

コードを生成するコードを書くことはメタプログラミングと呼ばれるものであり、Python はこのアプローチにはあまり適していません。

C++ がメタプログラミングのサポートを主張していると聞いたことがあるかもしれませんが、実際には、または定数固定構造に置き換えることができるテンプレートベースのメタプログラミングのみです。SFINAEルールのような「高度なメタプログラミング」を使用していくつかのトリックをプレイすることはできますが、それらはトリックにすぎません。

Python は言語が静的に型付けされておらず、クロージャーがある (C++ は静的に型付けされており、クロージャーがない) ため、テンプレートは必要ありませんが、Python は一般的なメタプログラミング アプローチ (つまり、コードを生成または操作する一般的なコードを書く) には役立ちません。 )。

メタプログラミングに興味があるなら、選択する言語はおそらく Common Lisp でしょう。コードを書くコードを書くことは Lisp にとって特別なことではありません。もちろん、マクロ(コードを書く関数) を書くことは、Lisp を使っている通常の実行時関数よりも難しいですが、その困難はほとんど本質的なものです (問題は実際にはもっと難しいです)。 ) であり、言語の制限により人工的ではありません。

多かれ少なかれ「私は人々が私にお金を払ってくれるコードを書くコードを書くコードを書くコードを書いている」のような古い冗談があります。メタプログラミングは確かに最初のステップに過ぎず、その後、メタメタプログラミング (コードジェネレーターを書くコードを書くこと) などがあります。もちろん、事態はますます難しくなっています (ただし、もう一度問題が難しくなったためであり、恣意的に課せられた制限のためではありません...)。

于 2012-11-02T17:07:04.923 に答える
0

まず第一に、これはおそらく本当に悪い考えです。理由を確認するには、コメントと他の回答を読んでください。

しかし、あなたの質問に答えるために、これはうまくいくはずです(ただし、これはハックであり、奇妙な副作用があるかもしれません):

from types import MethodType

def myfunc(self, *args):
    for arg in args:
        print arg

class Binder(object):
    def __init__(self, test_cases):
        for test_case in test_cases:
            method = MethodType(myfunc, self, Binder)
            setattr(self, test_case, method)

この場合、関数への参照はハードコーディングされていますが、もちろん引数として渡すこともできます。

これは出力です:

>>> b = Binder(['a', 'b'])
>>> b.a(1, 2, 3)
1
2
3
>>> b.b('a', 20, None)
a
20
None
于 2012-11-02T17:18:31.747 に答える