2011-01-27 11 views
5

Uso Boto para acceder a Amazon S3. Y para cargar archivos puedo asignar una función de devolución de llamada. El problema es que no puedo acceder a las variables necesarias desde esa función de devolución de llamada hasta que las hago globales. Por otro lado, si los hago globales, también serán globales para todas las demás tareas de Apio (hasta que reinicie Apio), ya que la carga del archivo se ejecuta desde una tarea de Apio.¿Cómo acceder (y editar) variables desde una función de devolución de llamada?

Aquí hay una función que carga un archivo JSON con información sobre el progreso de conversión de video.

def upload_json(): 
    global current_frame 
    global path_to_progress_file 
    global bucket 
    json_file = Key(bucket) 
    json_file.key = path_to_progress_file 
    json_file.set_contents_from_string('{"progress": "%s"}' % current_frame, 
    cb=json_upload_callback, num_cb=2, policy="public-read") 

Y aquí son 2 funciones de devolución de llamada para subir marcos generados por ffmpeg durante la conversión de vídeo y un archivo JSON con la información de progreso.

# Callback functions that are called by get_contents_to_filename. 
# The first argument is representing the number of bytes that have 
# been successfully transmitted from S3 and the second is representing 
# the total number of bytes that need to be transmitted. 
def frame_upload_callback(transmitted, to_transmit): 
    if transmitted == to_transmit: 
     upload_json() 
def json_upload_callback(transmitted, to_transmit): 
    global uploading_frame 
    if transmitted == to_transmit: 
     print "Frame uploading finished" 
     uploading_frame = False 

En teoría, podría pasar la variable uploading_frame a la función upload_json, pero no iba a llegar a json_upload_callback ya que es ejecutado por Boto.

De hecho, podría escribir algo como esto.

In [1]: def make_function(message): 
    ...:  def function(): 
    ...:   print message 
    ...:  return function 
    ...: 

In [2]: hello_function = make_function("hello") 

In [3]: hello_function 
Out[3]: <function function at 0x19f4c08> 

In [4]: hello_function() 
hello 

Lo cual, sin embargo, no le permite editar el valor de la función, simplemente le permite leer el valor.

def myfunc(): 
    stuff = 17 
    def lfun(arg): 
     print "got arg", arg, "and stuff is", stuff 
    return lfun 

my_function = myfunc() 
my_function("hello") 

Esto funciona.

def myfunc(): 
    stuff = 17 
    def lfun(arg): 
     print "got arg", arg, "and stuff is", stuff 
     stuff += 1 
    return lfun 

my_function = myfunc() 
my_function("hello") 

Y esto da un UnboundLocalError: variable local 'cosas' a las que se hace referencia antes de la asignación.

Gracias.

+0

Su ejemplo no muestra cómo uploading_frame se utiliza, por lo que es difícil de entender por qué evento desea. Crear un ejemplo más simple sin ningún material relacionado con S3 que muestre más claramente el problema probablemente también ayude. – dkagedal

Respuesta

12

En Python 2.x los cierres son de solo lectura. Sin embargo, puede utilizar un cierre sobre un valor mutable ... es decir

def myfunc(): 
    stuff = [17] # <<---- this is a mutable object 
    def lfun(arg): 
     print "got arg", arg, "and stuff[0] is", stuff[0] 
     stuff[0] += 1 
    return lfun 

my_function = myfunc() 
my_function("hello") 
my_function("hello") 

Si está en su lugar utilizando Python 3.x nonlocal la palabra clave puede utilizarse para especificar que una variable utilizada en lectura/escritura en un cierre es no es un local, sino que debe ser capturado desde el ámbito de inclusión:

def myfunc(): 
    stuff = 17 
    def lfun(arg): 
     nonlocal stuff 
     print "got arg", arg, "and stuff is", stuff 
     stuff += 1 
    return lfun 

my_function = myfunc() 
my_function("hello") 
my_function("hello") 
+1

¡Muchas gracias! ¡Eso funciona! – aruseni

+0

En Python 3 los cierres no son de solo lectura, pero debe especificar las variables modificadas con 'no local'. –

0

Una forma sencilla de hacer estas cosas es el uso de una función local

def myfunc(): 
    stuff = 17 
    def lfun(arg): 
     print "got arg", arg, "and stuff is", stuff 
     stuff += 1 
    def register_callback(lfun) 

Esto creará una nueva función cada vez que llame myfunc, y será capaz de utilizar la copia local "cosas".

+0

Consulte la actualización anterior. – aruseni

+0

Gracias por la respuesta. – aruseni

+1

[Esto no funciona.] (Http://codepad.org/p3N4gXLC) –

3

Puede crear una función parcial a través de functools.partial. Esta es una forma de llamar a una función con algunas variables precocinadas en la llamada. Sin embargo, para hacer ese trabajo, necesitaría pasar un valor mutable, por ejemplo, una lista o dict, a la función, en lugar de simplemente un bool.

from functools import partial 
def callback(arg1, arg2, arg3): 
    arg1[:] = [False] 
    print arg1, arg2, arg3 

local_var = [True] 
partial_func = partial(callback, local_var) 

partial_func(2, 1) 
print local_var # prints [False] 
+0

¡Muchas gracias! ¡Eso funciona! – aruseni

Cuestiones relacionadas