J'ai besoin de générer du code pour une méthode au moment de l'exécution. Il est important de pouvoir exécuter du code arbitraire et une docstring.
Je suis venu avec une solution combinant exec
et setattr
, voici un exemple factice:
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__
Existe-t-il un moyen meilleur/plus sûr/plus idiomatique de parvenir au même résultat?
Basé sur le code de Theran, mais en l'étendant aux méthodes sur les classes:
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()
Ce qui devrait imprimer:
in dynamo 0
in dynamo 1
Les documents de fonction et les noms sont des propriétés mutables. Vous pouvez faire ce que vous voulez dans la fonction interne, ou même avoir plusieurs versions de la fonction interne que makedynamo () choisit. Pas besoin de construire du code avec des chaînes.
Voici un extrait de l'interprète:
>>> 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 vous laissera déclarer une fonction dans une fonction, vous n’aurez donc pas à vous tromper 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)
Si vous souhaitez avoir plusieurs versions de la fonction, vous pouvez mettre tout cela en boucle et modifier le nom que vous leur donnez dans la fonction 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
(Je sais que ce n'est pas un bon code, mais il fait passer le message). En ce qui concerne la configuration de la docstring, je sais que c'est possible, mais il faudrait que je vérifie dans la documentation.
Edit : Vous pouvez définir la docstring via dynamo.__doc__
, de sorte que vous puissiez faire quelque chose comme ceci dans votre corps de boucle:
dynamo.__doc__ = "Adds %s to the weight" % i
Another Edit : Avec l’aide de @eliben et @bobince, le problème de fermeture devrait être résolu.
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()