如何访问(和编辑)回调函数中的变量?
我在用Boto访问亚马逊的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")
接下来是两个回调函数,用于上传在视频转换过程中由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
理论上,我可以把上传帧的变量传递给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
做这些事情的一个简单方法是使用一个本地函数。
def myfunc():
stuff = 17
def lfun(arg):
print "got arg", arg, "and stuff is", stuff
stuff += 1
def register_callback(lfun)
每次你调用myfunc的时候,它都会创建一个新的函数,并且这个新函数可以使用本地的“东西”的副本。
4
你可以通过 functools.partial
来创建一个部分函数。这是一种在调用函数时,提前把一些变量放进去的方法。不过,要让这个方法有效,你需要把一个可变的值,比如列表或字典,传递给函数,而不是仅仅传一个布尔值。
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]
16
在Python 2.x中,闭包中的变量是只读的(这不是因为Python虚拟机的原因,而是因为语法不允许对非本地和非全局变量进行写操作)。
不过,你可以在闭包中使用可变的值,也就是说……
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")