Cómo crear dinámicamente las funciones de nivel de módulo de métodos de una clase
-
01-10-2019 - |
Pregunta
Estoy intentando crear dinámicamente las funciones de nivel de módulo a partir de los métodos de una clase. Así, por cada método en una clase, quiero crear una función con el mismo nombre que crea una instancia de la clase y luego llama al método.
La razón por la que quiero hacer esto es por lo que puede adoptar un enfoque orientado a objetos para la creación de archivos de la tela. Desde Tela llamará a las funciones de nivel de módulo, pero no métodos de una clase, este es mi trabajo en torno.
He utilizado los siguientes enlaces de ayuda para empezar
- ¿Cómo llego lista de métodos en una clase de Python?
- dinámicamente la adición de funciones a un módulo de Python
- Como llamar en setattr () en el módulo actual ?
- http://effbot.org/zone/python-getattr.htm
- Llamar a una función de una módulo de una cadena con el nombre de la función en Python
- Cómo modificar el espacio de nombres local en pitón
Y tengo que subir 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 sale 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 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/
Solución
Esta sobre-pensar 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 hacer un nuevo objeto de función, que ya tiene uno regresó por getattr en su objeto. El método vinculado devuelto por getattr es algo exigible. Sólo asignarlo a su atributo de módulo, y que son buenos para ir.
Otros consejos
En realidad el código es correcto, pero cuando func retorno (* args, **) realiza kwargs, args pasarán tupla vacía como () y no existen parámetros en el método 2, por lo que plantea como excepción,
una solución rápida a 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'