Creación dinámica de funciones en Python con nombres personalizados.

Disculpas si esta pregunta ya ha sido planteada y contestada. Lo que debo hacer es un concepto muy simple, pero desafortunadamente no he podido encontrar una respuesta en línea.

Necesito crear funciones dinámicas en Python (Python2.7) con nombres personalizados en tiempo de ejecución. El cuerpo de cada función también debe construirse en tiempo de ejecución, pero es (casi) el mismo para todas las funciones.

Empiezo con una lista de nombres.

func_names = ["func1", "func2", "func3"] 

Tenga en cuenta que la lista func_name puede contener una lista de nombres arbitrarios, por lo que los nombres NO serán simplemente func1, func2, func3, …

Quiero que el resultado sea:

  def func1(*args): ... def func2(*args): ... def func3(*args): ... 

La razón por la que necesito hacer esto es que cada nombre de función corresponde a un caso de prueba que luego se llama desde el mundo exterior.

actualización : No hay entrada de usuario. Estoy atando dos extremos de un módulo mucho más grande. Un extremo determina cuáles son los casos de prueba y, entre otras cosas, rellena una lista de los nombres de los casos de prueba. El otro extremo son las funciones en sí, que deben tener una asignación 1: 1 con el nombre del caso de prueba. Así que tengo el nombre de los casos de prueba, sé lo que quiero hacer con cada caso de prueba, solo necesito crear las funciones que tienen el nombre de los casos de prueba. Dado que el nombre de los casos de prueba se determina en tiempo de ejecución, la creación de la función basada en esos casos de prueba también debe ser en tiempo de ejecución.

actualización : también puedo envolver estas funciones personalizadas con nombre en una clase si eso facilitaría las cosas.

Puedo codificar el contenido de las funciones (ya que son casi las mismas) en una cadena, o puedo basarlo en una clase base previamente definida. Solo necesita saber cómo llenar las funciones con este contenido.

Por ejemplo:

  func_content = """ for arg in args: print arg """ 

Gracias por adelantado,

Mahdi

Para lo que describe, no creo que deba descender a evaluaciones o macros: crear instancias de funciones por cierre debería funcionar bien. Ejemplo:

 def bindFunction1(name): def func1(*args): for arg in args: print arg return 42 # ... func1.__name__ = name return func1 def bindFunction2(name): def func2(*args): for arg in args: print arg return 2142 # ... func2.__name__ = name return func2 

Sin embargo, es probable que desee agregar esas funciones por nombre a algún ámbito para que pueda acceder a ellas por nombre.

 >>> print bindFunction1('neat')  >>> print bindFunction2('keen')  

Ampliando la respuesta de Shane ya que acabo de encontrar esta pregunta cuando busco una solución a un problema similar. Tenga cuidado con el scope de las variables. Puede evitar problemas de scope utilizando una función de generador para definir el scope. Aquí hay un ejemplo que define métodos en una clase:

 class A(object): pass def make_method(name): def _method(self): print("method {0} in {1}".format(name, self)) return _method for name in ('one', 'two', 'three'): _method = make_method(name) setattr(A, name, _method) 

En uso:

 In [4]: o = A() In [5]: o.one() method one in <__main__.A object at 0x1c0ac90> In [6]: o1 = A() In [7]: o1.one() method one in <__main__.A object at 0x1c0ad10> In [8]: o.two() method two in <__main__.A object at 0x1c0ac90> In [9]: o1.two() method two in <__main__.A object at 0x1c0ad10> 

Probablemente haya una especie de introspección para hacer este tipo de cosas, pero no creo que sea el enfoque pythonico del problema.

Creo que deberías aprovechar la naturaleza de las funciones en Python como ciudadanos de primer nivel. Use los cierres como Shane Holloway señaló, para personalizar el contenido de la función a su gusto. Luego, para la vinculación dinámica de nombres, use un diccionario cuyas claves sean los nombres definidos dinámicamente, y los valores serán las funciones en sí.

 def function_builder(args): def function(more_args): #do stuff based on the values of args return function my_dynamic_functions = {} my_dynamic_functions[dynamic_name] = function_builder(some_dynamic_args) #then use it somewhere else my_dynamic_functions[dynamic_name](the_args) 

Espero que tenga sentido para su caso de uso.

Es posible que desee utilizar eval ; usted construirá la cadena que contiene la definición de Python de cada función (es decir, una cadena multilínea que comienza con def func1 ….) y luego la eval .

Pero generaría un nombre único para cada una de esas funciones (por ejemplo, genfun345 ). No utilice una entrada de usuario sin marcar para dichos nombres. Porque si el nombre es el mismo que el de un nombre conocido en Python, estarás en un desastre difícil de depurar.

¿Confías en las entradas a partir de las cuales se realizan estas funciones? ¿Te importa el malware o el abuso?

Lea acerca de las macros higiénicas en wikipedia.

Si entiendo sus requisitos correctamente, parece que solo desea asignar dinámicamente las funciones existentes o los nombres alternativos, en cuyo caso, algo parecido a lo siguiente debe hacer el trabajo:

 import sys testcases = [] def testcase(f): """ testcase function decorator """ testcases.append(f) return f @testcase def testcase0(*args): print 'testcase0 called, args:', args @testcase def testcase1(*args): print 'testcase1 called, args:', args @testcase def testcase2(*args): print 'testcase2 called, args:', args def assign_function_names(func_names, namespace=None): if namespace is None: namespace = sys._getframe(1).f_globals # default to caller's globals for name, func in zip(func_names, testcases): func.__name__ = name # optional namespace[name] = func assign_function_names(["funcA", "funcB", "funcC"]) funcA(1, 2, 3) funcB(4, 5) funcC(42) 

Para crear funciones de forma dinámica, puedes usar makefun , lo he desarrollado solo para eso. Soporta tres formas de definir la firma a generar:

  • una representación de cadena, por ejemplo 'foo(a, b=1)'
  • un objeto de Signature , ya sea artesanal o derivado utilizando inspect.signature en otra función
  • Una función de referencia. En ese caso, la firma expuesta será idéntica a la firma de esta función.

Además, puede indicarle que inyecte la referencia de la función creada como el primer argumento en su implementación, a fin de que tenga modificaciones menores de comportamiento en función de la procedencia de la llamada (la fachada). Por ejemplo:

 # generic core implementation def generic_impl(f, *args, **kwargs): print("This is generic impl called by %s" % f.__name__) # here you could use f.__name__ in a if statement to determine what to do if f.__name__ == "func1": print("called from func1 !") return args, kwargs my_module = getmodule(generic_impl) # generate 3 facade functions with various signatures for f_name, f_params in [("func1", "b, *, a"), ("func2", "*args, **kwargs"), ("func3", "c, *, a, d=None")]: # the signature to generate f_sig = "%s(%s)" % (f_name, f_params) # create the function dynamically f = create_function(f_sig, generic_impl, inject_as_first_arg=True) # assign the symbol somewhere (local context, module...) setattr(my_module, f_name, f) # grab each function and use it func1 = getattr(my_module, 'func1') assert func1(25, a=12) == ((), dict(b=25, a=12)) func2 = getattr(my_module, 'func2') assert func2(25, a=12) == ((25,), dict(a=12)) func3 = getattr(my_module, 'func3') assert func3(25, a=12) == ((), dict(c=25, a=12, d=None)) 

Como puede ver en la documentación , los argumentos siempre se redirigen a los kwargs excepto cuando no es posible (firmas de posición func2 , como en func2 ).