2010-03-25 15 views
15

¿Puedo definir una función que, cuando se invoca, inserta locales nuevos en el alcance de la persona que llama? Tengo la sensación de que la aprobación de locales del llamante() en la función que podría funcionar, pero ¿hay una manera de hacer lo que quiera sin tener que hacer esto?¿Inyectar variables en el alcance de la persona que llama?

+4

Supongamos que se espera que la persona que llama comprenda los riesgos de que el destinatario acuda a la ciudad con sus locales. – jogloran

+0

Suena como una receta para espaguetis –

+11

Se llama "devolución". –

Respuesta

8

Por reglas de Python, no se puede alterar de locals su interlocutor; en las implementaciones actuales, si se intenta (por ejemplo, con la magia negro Anurag sugiere) no obtendrá una excepción (aunque me gustaría añadir que el registro de error a alguna versión futura), pero será esencialmente inoperante si la persona que llama es una función (no si la persona que llama es el código de nivel superior del módulo): las variables locales reales de la persona que llama no se verán afectadas de hecho. Esto es válido si se pasan de forma explícita de la persona que llama en locals, o descabellada través de la magia negro: que todavía tienen que ser tratados como un solo lectura dict si el código ha de tener alguna cordura.

Más bien, podría hacer que la persona que llama pase un dict explícito, real, normal (que se puede inicializar desde locals() si lo desea), y todas las alteraciones que haga su código en esa frase estarán allí para su uso. - pero no tan "nuevos" en barenames ámbito local de la persona que llama, por supuesto, pero la funcionalidad es la misma tanto si la persona que llama tiene que utilizar x['foo'] o x.foo o (como se prefiere) justo barename foo.

Por cierto, para usar la sintaxis de atributo de acceso en lugar de la sintaxis de indexación dict, que puede hacer:

class Bunch(object): pass 

... 

# caller code 
b = Bunch() 
thefun(b) 
print b.foo 

... 

# called function 
def thefun(b): 
    b.foo = 23 

Esto también cubre, con una pequeña variación, el caso en el que thefun quiere trabajar con la sintaxis de indexación dict (digamos que su cuerpo es b['foo'] = 23 en lugar de b.foo = 23): en ese caso, la persona que llama solo necesita usar thefun(vars(b)) en lugar del thefun(b) normal, pero luego puede seguir trabajando con la sintaxis de acceso b.foo.

+0

Bueno, parece que no es posible hacer lo que quiero. Gracias por la idea de acceso al atributo; si no puedo tener nombres desnudos (x), la siguiente cosa más bonita es probablemente el acceso a los atributos (context.x). – jogloran

+0

Esto no es cierto, consulte la respuesta a continuación utilizando el módulo de inspección incorporado de python para ver cómo se hace. Python es un lenguaje dinámico completo que puede sobrescribir su propio intérprete si así lo desea. Solo cito http://xkcd.com/353/ – Pykler

+0

Alex es correcto. La respuesta de @ Pykler solo funciona en un caso específico, y no en general. – ferrouswheel

17

Mira la inspect module, que es utilizado por minimock burlarse alcance de la persona que llama.

Este código debe hacer exactamente lo que quiere:

import inspect 
def mess_with_caller(): 
    stack = inspect.stack() 
    try: 
     locals_ = stack[1][0].f_locals 
    finally: 
     del stack 
    locals_['my_new_function'] = lambda : 'yaaaaaay' 


mess_with_caller() 
print my_new_function() 
>>> Output: 'yaaaaaay' 
+0

FYI: inspect es un módulo de python integrado. – Pykler

+0

¡Contempla el poder de Python! – Pykler

+2

Magia negra espeluznante, pero genial, no obstante. – Cerin

0

Esto no es posible sin cambiar la API, como CPython conserva una referencia a la lista de argumentos para la duración de la llamada de función.

Una forma de hacer esto es pasar un objeto y desactive el estado interno del objeto cuando se hace uso de ella.

class Obj(object): 
    def __init__(self, state): 
     self.state = state 

def f(o): 
    # Do stuff with o.state 
    del o.state 
Cuestiones relacionadas