2010-09-08 18 views
8

Estoy intentando crear dinámicamente funciones de nivel de módulo a partir de los métodos de una clase. Entonces, para cada método en una clase, quiero crear una función con el mismo nombre que cree una instancia de la clase y luego llame al método.Cómo crear dinámicamente funciones de nivel de módulo a partir de métodos en una clase

La razón por la que quiero hacer esto es para que pueda adoptar un enfoque orientado a objetos para crear archivos Fabric. Dado que Fabric llamará a funciones de nivel de módulo pero no a métodos de una clase, esta es mi solución.

He utilizado los siguientes enlaces de ayuda para empezar

Y he llegado con el siguiente código

import inspect 
import sys 
import types 

class TestClass(object): 
    def __init__(self): 
     pass 

    def method1(self, arg1): 
     print 'method 1 %s' % arg1 

    def method2(self): 
     print 'method 2' 

def fabric_class_to_function_magic(module_name): 
    # get the module as an object 
    print module_name 
    module_obj = sys.modules[module_name] 
    print dir(module_obj) 

    # Iterate over the methods of the class and dynamically create a function 
    # for each method that calls the method and add it to the current module 
    for method in inspect.getmembers(TestClass, predicate=inspect.ismethod): 
     print 
     print method 
     method_name, method_obj = method 

     # create a new template function which calls the method 
     def newfunc_template(*args, **kwargs): 
      tc = TestClass() 
      func = getattr(tc, method_name) 
      return func(*args, **kwargs) 

     # create the actual function 
     print 'code: ', newfunc_template.func_code 
     print 'method_name: ', method_name 
     newfunc = types.FunctionType(newfunc_template.func_code, 
            {'TestClass': TestClass, 
             'getattr': getattr, 
             'method_name': method_name, 
             }, 
            name=method_name, 
            argdefs=newfunc_template.func_defaults, 
            closure=newfunc_template.func_closure, 
            ) 

     # add the new function to the current module 
     setattr(module_obj, method_name, newfunc) 

# test the dynamically created module level function 
thismodule = sys.modules[__name__] 
print dir(thismodule) 
fabric_class_to_function_magic(__name__) 
print dir(thismodule) 
method1('arg1') 
method2() 

Y me da el siguiente error

['TestClass', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'sys', 'thismodule', 'types'] 
__main__ 
['TestClass', '__builtins__', '__doc__', '__file__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'sys', 'thismodule', 'types'] 

('__init__', <unbound method TestClass.__init__>) 
code: <code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85> 
method_name: __init__ 

('method1', <unbound method TestClass.method1>) 
code: <code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85> 
method_name: method1 

('method2', <unbound method TestClass.method2>) 
code: <code object newfunc_template at 0x7f8800a28d50, file "test.py", line 85> 
method_name: method2 
['TestClass', '__builtins__', '__doc__', '__file__', '__init__', '__name__', '__package__', 'fabric_class_to_function_magic', 'inspect', 'method1', 'method2', 'sys', 'thismodule', 'types'] 
Traceback (most recent call last): 
    File "test.py", line 111, in <module> 
    method1('arg1') 
    File "test.py", line 88, in newfunc_template 
    return func(*args, **kwargs) 
TypeError: method2() takes exactly 1 argument (2 given) 

Parece ser que la reutilización de la referencia a la función? ¿Algunas ideas?

ACTUALIZACIÓN: Aquí está el código de trabajo con arreglo a Ned Batchelder

def fabric_class_to_function_magic(module_name): 
    # get the module as an object 
    module_obj = sys.modules[module_name] 

    # Iterate over the methods of the class and dynamically create a function 
    # for each method that calls the method and add it to the current module 
    for method in inspect.getmembers(TestClass, predicate=inspect.ismethod): 
     method_name, method_obj = method 

     # get the bound method 
     tc = TestClass() 
     func = getattr(tc, method_name) 

     # add the function to the current module 
     setattr(module_obj, method_name, func) 

ACTUALIZACIÓN 2: Aquí está mi blog sobre el tema: http://www.saltycrane.com/blog/2010/09/class-based-fabric-scripts-metaprogramming-hack/

+0

por extraño que parezca, también debe establecer 'func .__ module__ = module_name' para que se describa correctamente ... de lo contrario, informará su módulo como el que contiene' fabric_class_to_function_magic' en lugar del módulo de destino. – F1Rumors

Respuesta

9

Está pensando demasiado en su solución. Cambiar el final de fabric_class_to_function_magic ser la siguiente:

tc = TestClass() 
    func = getattr(tc, method_name) 

    # add the new function to the current module 
    setattr(module_obj, method_name, func) 

y funciona bien. No es necesario crear un nuevo objeto de función, ya tiene uno devuelto por getattr en su objeto. El método consolidado devuelto por getattr es una cosa invocable. Simplemente asignes a tu atributo de módulo, y estás listo para continuar.

1

realidad su código es correcto, pero cuando func retorno (* args, ** kwargs) se ejecuta, args pasará una tupla vacía como() y no hay parámetros en su method2, por lo que plantea dicha excepción,

una solución rápida para su problema sería, como

class TestClass(object): 
    def __init__(self): 
     pass 

    def method1(self, arg1): 
     print 'method 1 %s' % arg1 

    def method2(self, *args, **kw): 
     print 'method 2' 
+0

Esto hace que el error desaparezca, pero el problema subyacente aún existe. Ahora imprime "método 2" dos veces. – saltycrane

Cuestiones relacionadas