Introspección de funciones anidadas (locales) de una función determinada en Python
-
22-07-2019 - |
Pregunta
Dada la función
def f():
x, y = 1, 2
def get():
print 'get'
def post():
print 'post'
¿hay alguna forma de acceder a sus funciones locales get () y post () de forma que pueda llamarlas? Estoy buscando una función que funcione así con la función f () definida anteriormente:
>>> get, post = get_local_functions(f)
>>> get()
'get'
Puedo acceder a los objetos de código para esas funciones locales así
import inspect
for c in f.func_code.co_consts:
if inspect.iscode(c):
print c.co_name, c
que da como resultado
get <code object get at 0x26e78 ...>
post <code object post at 0x269f8 ...>
pero no puedo entender cómo obtener los objetos de función invocables reales. ¿Es eso posible?
Gracias por su ayuda,
Will.
Solución
Estás bastante cerca de hacerlo, solo falta el módulo new
:
import inspect
import new
def f():
x, y = 1, 2
def get():
print 'get'
def post():
print 'post'
for c in f.func_code.co_consts:
if inspect.iscode(c):
f = new.function(c, globals())
print f # Here you have your function :].
¿Pero por qué se molestan? ¿No es más fácil usar la clase? La creación de instancias parece una llamada de función de todos modos.
Otros consejos
Puede devolver funciones como cualquier otro objeto en Python:
def f():
x, y = 1, 2
def get():
print 'get'
def post():
print 'post'
return (get, post)
get, post = f()
¡Espero que esto ayude!
Tenga en cuenta, sin embargo, que si desea utilizar sus variables 'x' e 'y' en get () o post (), debe hacerlas una lista.
Si haces algo como esto:
def f():
x = [1]
def get():
print 'get', x[0]
x[0] -= 1
def post():
print 'post', x[0]
x[0] += 1
return (get, post)
get1, post1 = f()
get2, post2 = f()
get1 y post1 harán referencia a una lista 'x' diferente de get2 y post2.
Podría usar exec para ejecutar los objetos de código. Por ejemplo, si tenía f definida como anteriormente, entonces
exec(f.func_code.co_consts[3])
daría
get
como salida.
Los objetos de la función interna no existen antes de que se ejecute la función f (). Si desea obtenerlos, deberá construirlos usted mismo. Eso definitivamente no es trivial porque podrían ser cierres que capturan variables del alcance de la función y de todos modos requerirán hurgar en objetos que probablemente deberían considerarse como detalles de implementación del intérprete.
Si desea recopilar las funciones con menos repetición, le recomiendo uno de los siguientes enfoques:
a) Simplemente ponga las funciones en una definición de clase y devuelva una referencia a esa clase. Una colección de funciones relacionadas a las que se accede por nombre huele terriblemente a una clase.
b) Cree una subclase dict que tenga un método para registrar funciones y úsela como decorador.
El código para esto se vería así:
class FunctionCollector(dict):
def register(self, func):
self[func.__name__] = func
def f():
funcs = FunctionCollector()
@funcs.register
def get():
return 'get'
@funcs.register
def put():
return 'put'
return funcs
c) Examine los locales () y filtre la función con inspect.isfunction. (generalmente no es una buena idea)