Как получить доступ (и редактировать) переменные из функции обратного вызова? - PullRequest
5 голосов
/ 27 января 2011

Я использую Boto для доступа к Amazon S3. А для загрузки файлов я могу назначить функцию обратного вызова. Проблема в том, что я не могу получить доступ к необходимым переменным из этой функции обратного вызова, пока я не сделаю их глобальными. С другой стороны, если я сделаю их глобальными, они будут глобальными и для всех других задач Celery (до тех пор, пока я не перезапущу Celery), поскольку загрузка файла выполняется из задачи Celery.

Вот функция, которая загружает файл JSON с информацией о ходе преобразования видео.

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")

А вот 2 функции обратного вызова для загрузки кадров, сгенерированных ffmpeg во время преобразования видео, и файл JSON с информацией о ходе выполнения.

# 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

Теоретически, я мог бы передать переменную uploading_frame в функцию upload_json, но она не попала бы в json_upload_callback, поскольку она выполняется Boto.

На самом деле, я мог бы написать что-то вроде этого.

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

Что, однако, не позволяет вам редактировать значение из функции, просто позволяет вам прочитать значение.

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

my_function = myfunc()
my_function("hello")

Это работает.

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")

И это дает UnboundLocalError: локальную переменную 'stuff', на которую ссылаются перед присваиванием.

Спасибо.

Ответы [ 3 ]

13 голосов
/ 27 января 2011

В Python 2.x замыкания доступны только для чтения.Однако вы можете использовать замыкание над изменяемым значением ... т.е.

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")

Если вы вместо этого используете Python 3.x, ключевое слово nonlocal может использоваться для указания того, что переменная используется для чтения / записи.в замыкании не является локальным, но должен быть захвачен из охватывающей области:

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")
3 голосов
/ 27 января 2011

Вы можете создать частичную функцию через functools.partial. Это способ вызова функции с некоторыми переменными, предварительно запеченными в вызове. Однако, чтобы это работало, вам нужно передать изменяемое значение - например, список или dict - в функцию, а не просто в 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 голосов
/ 27 января 2011

Простой способ сделать это - использовать локальную функцию

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

Это создаст новую функцию каждый раз, когда вы вызываете myfunc, и она сможет использовать локальную копию «вещи».

...