2009-08-05 10 views
5

Dada la funciónIntrospección de funciones anidadas (locales) de una función dada en Python

def f(): 
    x, y = 1, 2 
    def get(): 
     print 'get' 
    def post(): 
     print 'post' 

¿hay alguna manera de acceder a su get local() y post() funciona de una manera que puedo llamarlos ? Busco una función que va a funcionar como tal con la función f() definida anteriormente:

>>> get, post = get_local_functions(f) 
>>> get() 
'get' 

puedo tener acceso al código de objetos para esas funciones locales al igual que

import inspect 
for c in f.func_code.co_consts: 
    if inspect.iscode(c): 
     print c.co_name, c 

que se traduce en

get <code object get at 0x26e78 ...> 
post <code object post at 0x269f8 ...> 

pero no puedo encontrar la forma de obtener los objetos reales de la función invocable. ¿Es eso posible?

Gracias por su ayuda,

Will.

+0

Debo señalar que para mi caso de uso real, tengo que ser capaz de llamar a las funciones locales/anidados de esta manera: get (solicitud, * args, ** kwargs) –

Respuesta

4

Está bastante cerca de hacerlo - sólo falta new módulo:

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é diablos molesta? ¿No es más fácil usar la clase? La creación de instancias parece una llamada a función de todos modos.

+0

Eso funciona, siempre que las funciones anidadas no sean cierres. ¡Gracias! Usted y Anas Aasma tienen razón, sin embargo, de que solo debería usar una clase para esto. La razón por la que no estoy es porque este es un proyecto de Django que ya tiene muchos decoradores de visualización utilizados para la autenticación personalizada, etc., y no quería adaptar todos esos decoradores para trabajar con métodos. Así que estoy medio asiduo simulando un enfoque basado en clases para vistas RESTful (como http://github.com/jpwatts/django-restviews/tree/master). –

+0

Creo que esto solo funciona porque tienes f en el módulo actual. – Quant

2

Puede volver funciones al igual que 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!

Sin embargo, tenga en cuenta que si desea utilizar las variables 'x' e 'y' en get() o publicar(), 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 diferente 'x' que get2 y post2.

+0

sí, eso es en realidad lo que estoy haciendo en este momento. Esperaba encontrar una manera de evitar devolver las funciones explícitamente, si es posible. –

+3

Will, para citar el ** Zen de Python **: * "Explicit es mejor que implícito." * --and-- * "Los casos especiales no son lo suficientemente especiales como para romper las reglas." * –

+0

Buen punto, Evan . La solución que tengo ahora, que es esencialmente la misma excepto que requiere devolver un dict en lugar de una tupla, funciona bien. Solo esperaba evitar la repetición de poner return dict (get = get, post = post, put = put, etc) en la parte inferior de un grupo de vistas de Django. Gracias por el consejo, sin embargo. –

2

Puede usar exec para ejecutar los objetos de código. Por ejemplo, si se hubiera definido f de arriba, luego

exec(f.func_code.co_consts[3]) 

daría

get 

como salida.

+0

¡Ah, así es como se ejecuta un objeto de código! Desafortunadamente, para mi caso de uso real, necesito poder pasar argumentos a las funciones locales. ¿Hay alguna manera de hacerlo cuando llamo a exec()? –

1

Los objetos de la función interna no existen antes de que se ejecute la función f(). Si quieres obtenerlos, tendrás que construirlos tú 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 recomiendo uno de los siguientes enfoques:

a) Sólo hay que poner las funciones en una definición de clase y devolver una referencia a esa clase. Una colección de funciones relacionadas a las que se accede por su nombre huele terriblemente como una clase.

b) Cree una subclase dict que tenga un método para registrar funciones y utilícela como decorador.

El código para esto sería algo como esto:

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) hurgar en los locales() y filtrar la función con inspect.isfunction. (Por lo general no es una buena idea)

+0

Re: "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 su nombre huele terriblemente como una clase". Definitivamente tiene razón. Vea mi comentario sobre la respuesta de gavoja a continuación para obtener una explicación de por qué no estoy usando una clase en este caso. ¡Gracias por tus explicaciones, sin embargo! –

Cuestiones relacionadas