如何从回调函数访问(和编辑)变量?

时间:2011-01-27 10:46:17

标签: python variables namespaces callback closures

我使用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

理论上,我可以将uploaded_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 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分:3)

您可以通过functools.partial创建部分功能。这是一种调用函数的方法,其中一些变量预先烘焙到调用中。但是,为了完成这项工作,你需要将一个可变值 - 例如列表或字典 - 传递给函数,而不仅仅是一个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]

答案 2 :(得分:0)

执行这些操作的一种简单方法是使用本地函数

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

每次调用myfunc时都会创建一个新函数,它将能够使用本地“stuff”副本。