web-dev-qa-db-ja.com

Pythonでの動的/実行時メソッド作成(コード生成)

実行時にメソッドのコードを生成する必要があります。任意のコードを実行でき、docstringを持つことが重要です。

execsetattrを組み合わせたソリューションを思い付きました。ここにダミーの例を示します。

class Viking(object):
    def __init__(self):
        code = '''
            def dynamo(self, arg):
                """ dynamo's a dynamic method!
                """
                self.weight += 1
                return arg * self.weight
            '''
        self.weight = 50

        d = {}
        exec code.strip() in d
        setattr(self.__class__, 'dynamo', d['dynamo'])


if __== "__main__":
    v = Viking()
    print v.dynamo(10)
    print v.dynamo(10)
    print v.dynamo.__doc__

同じ結果を達成するためのより良い/安全な/より慣用的な方法はありますか?

42
Eli Bendersky

Theranのコードに基づきますが、クラスのメソッドに拡張します:



class Dynamo(object):
    pass

def add_dynamo(cls,i):
    def innerdynamo(self):
        print "in dynamo %d" % i
    innerdynamo.__doc__ = "docstring for dynamo%d" % i
    innerdynamo.__= "dynamo%d" % i
    setattr(cls,innerdynamo.__name__,innerdynamo)

for i in range(2):
    add_dynamo(Dynamo, i)

d=Dynamo()
d.dynamo0()
d.dynamo1()

印刷する必要があります:


in dynamo 0
in dynamo 1
71
John Montgomery

関数のドキュメント文字列と名前は変更可能なプロパティです。内部関数で必要なことは何でもできますし、makedynamo()が選択する複数のバージョンの内部関数を持つこともできます。文字列からコードを作成する必要はありません。

インタープリターの抜粋は次のとおりです。

>>> def makedynamo(i):
...     def innerdynamo():
...         print "in dynamo %d" % i
...     innerdynamo.__doc__ = "docstring for dynamo%d" % i
...     innerdynamo.__= "dynamo%d" % i
...     return innerdynamo

>>> dynamo10 = makedynamo(10)
>>> help(dynamo10)
Help on function dynamo10 in module __main__:

dynamo10()
    docstring for dynamo10
12
Theran

Pythonでは、関数内で関数を宣言できるため、execトリックを行う必要はありません。

def __init__(self):

    def dynamo(self, arg):
        """ dynamo's a dynamic method!
        """
        self.weight += 1
        return arg * self.weight
    self.weight = 50

    setattr(self.__class__, 'dynamo', dynamo)

関数のいくつかのバージョンが必要な場合は、これらすべてをループに入れて、setattr関数でそれらの名前を変更できます。

def __init__(self):

    for i in range(0,10):

        def dynamo(self, arg, i=i):
            """ dynamo's a dynamic method!
            """
            self.weight += i
            return arg * self.weight

        setattr(self.__class__, 'dynamo_'+i, dynamo)
        self.weight = 50

(これは素晴らしいコードではないことは知っていますが、重要な点は理解できます)。 docstringを設定する限り、それが可能であることは知っていますが、ドキュメントで調べる必要があります。

編集dynamo.__doc__を介してdocstringを設定できるため、ループ本体で次のようなことができます。

dynamo.__doc__ = "Adds %s to the weight" % i

別の編集:@elibenと@bobinceの助けを借りて、クロージャーの問題を解決する必要があります。

8
Justin Voss
class Dynamo(object):
    def __init__(self):
        pass

    @staticmethod
    def init(initData=None):
        if initData is not None:
            dynamo= Dynamo()
            for name, value in initData.items():
                code = '''
def %s(self, *args, **kwargs):
%s
                            ''' % (name, value)
                result = {}
                exec code.strip() in result
                setattr(dynamo.__class__, name, result[name])

            return dynamo

        return None

service = Dynamo.init({'fnc1':'pass'})
service.fnc1()
0
Alexander