2010-09-08 22 views
8

J'essaie de créer dynamiquement des fonctions de niveau module à partir des méthodes d'une classe. Donc, pour chaque méthode dans une classe, je veux créer une fonction avec le même nom qui crée une instance de la classe, puis appelle la méthode.Comment créer dynamiquement des fonctions de niveau module à partir des méthodes d'une classe

La raison pour laquelle je veux faire ceci est que je peux adopter une approche orientée objet pour créer des fichiers Fabric. Puisque Fabric appellera des fonctions de niveau de module mais pas des méthodes d'une classe, c'est mon work-around.

J'ai utilisé les liens suivants pour me commencé

Et je suis venu avec le code suivant

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() 

et je reçois l'erreur suivante

['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) 

Il semble réutiliser la référence à la fonction? Des idées?

MISE À JOUR: Voici le code de travail avec la solution de 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) 

MISE À JOUR 2: Voici mon blog sur le sujet: http://www.saltycrane.com/blog/2010/09/class-based-fabric-scripts-metaprogramming-hack/

+0

bizarrement, vous devez également définir 'func .__ module__ = nom_module' pour qu'il se décrive correctement ... sinon il signalera son module comme celui contenant' fabric_class_to_function_magic' au lieu du module cible! – F1Rumors

Répondre

9

Vous pensez trop votre solution. Changer la fin de fabric_class_to_function_magic être ceci:

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

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

et il fonctionne très bien. Pas besoin de faire un nouvel objet fonction, vous en avez déjà un retourné par getattr sur votre objet. La méthode liée renvoyée par getattr est une chose appelable. Affectez-le simplement à votre attribut de module, et vous êtes prêt à partir.

1

fait votre code est bon, mais lorsque le retour func (* args, ** kwargs) exécute, args passera vide tuple like() et il n'y a pas de paramètres dans votre méthode2, donc il soulève une telle exception,

une solution rapide vers votre problème serait, comme

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

Cela fait disparaître l'erreur, mais le problème sous-jacent existe toujours. Maintenant, il imprime "méthode 2" deux fois. – saltycrane