実行時にメソッドのコードを生成する必要があります。任意のコードを実行でき、docstringを持つことが重要です。
exec
とsetattr
を組み合わせたソリューションを思い付きました。ここにダミーの例を示します。
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__
同じ結果を達成するためのより良い/安全な/より慣用的な方法はありますか?
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
関数のドキュメント文字列と名前は変更可能なプロパティです。内部関数で必要なことは何でもできますし、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
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の助けを借りて、クロージャーの問題を解決する必要があります。
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()