Python闭包,局部变量作用域错误
我的函数在赋值前向我抛出引用的Python闭包,局部变量作用域错误,python,closures,Python,Closures,我的函数在赋值前向我抛出引用的局部变量“pt”错误: Traceback (most recent call last): File "/home/solesschong/Workspace/PenPal/python/main.py", line 126, in callback ind = (i+pt) % n UnboundLocalError: local variable 'pt' referenced before assignment 代码如下 def get_aud
局部变量“pt”
错误:
Traceback (most recent call last):
File "/home/solesschong/Workspace/PenPal/python/main.py", line 126, in callback
ind = (i+pt) % n
UnboundLocalError: local variable 'pt' referenced before assignment
代码如下
def get_audio_callback(pt):
def callback(in_data, frame_count, time_info, status):
for i in range(frame_count):
ind = (i+pt) % n
return (a, b)
return callback
在全球范围内,
pt = 0
stream = p.open(stream_callback=get_audio_callback(pt))
我无法找出错误发生的原因,因为我已经用一些闭包示例进行了检查,没有发现任何差异
编辑
您无法重现错误的原因可能是由于过度简化,正如@Martijn Pieters所提到的。
因此是原始代码
我通过引用进一步解决了这个问题,请看我自己的答案
"""
Sound API
"""
def get_audio_callback(pt):
def callback(in_data, frame_count, time_info, status):
"""
This is the callback function for sound API
In each call, synthesized data is dumpped into the sound buffer
"""
wave = np.ndarray((frame_count, 2))
for i in range(frame_count):
ind = (i+pt) % n
wave[i,0] = float(x[ind]) * 2
wave[i,1] = float(y[ind]) * 2
pt = pt + frame_count
return (encode(wave), pyaudio.paContinue)
return callback
p = pyaudio.PyAudio()
pt = 0
stream = p.open(format=pyaudio.paFloat32,
channels=2,
rate=RATE,
output=True,
stream_callback=get_audio_callback(pt))
原来是关于“参考”的问题 我将代码改为通过变量传递
pt
,结果很好
pt = [0]
def get_audio_callback(pt_ref):
def callback(in_data, frame_count, time_info, status):
pt = pt_ref[0]
for i in range(frame_count):
ind = (i+pt) % n
return (a, b)
return callback
您的代码在回调中分配给pt
;Python在编译时确定名称的范围,赋值使其成为本地名称
pt = pt + frame_count
除非您告诉Python其他情况,否则就是这样。在Python2中,只能将名称显式标记为全局
,而Python3需要能够使用非局部
关键字:
def callback(in_data, frame_count, time_info, status):
"""
This is the callback function for sound API
In each call, synthesized data is dumpped into the sound buffer
"""
nonlocal pt
wave = np.ndarray((frame_count, 2))
for i in range(frame_count):
ind = (i+pt) % n
wave[i,0] = float(x[ind]) * 2
wave[i,1] = float(y[ind]) * 2
pt = pt + frame_count
return (encode(wave), pyaudio.paContinue)
使用nonlocal pt
行,Python被明确告知不要将pt
视为本地名称,而是从get\u audio\u callback
的封闭范围中获取它
在Python 2中,您只需创建一个从闭包中获取其值的本地:
def callback(in_data, frame_count, time_info, status):
"""
This is the callback function for sound API
In each call, synthesized data is dumpped into the sound buffer
"""
pt_local = pt
wave = np.ndarray((frame_count, 2))
for i in range(frame_count):
ind = (i+pt_local) % n
wave[i,0] = float(x[ind]) * 2
wave[i,1] = float(y[ind]) * 2
pt_local = pt_local + frame_count
return (encode(wave), pyaudio.paContinue)
因为您的封闭get_audio_callback
范围似乎无论如何都不会使用pt
,并且不需要访问更新的pt_local
值
如果您确实需要pt
在get_audio\u callback
范围内进行更新(比如说,callback
被多次调用,并且您需要pt
从一个调用更新到另一个调用),那么您需要避免将pt
作为callback
函数中的一个本地函数使用
一个有效的解决方法是将值包装在一个可变对象中,或者将其作为可变属性分配给某个地方,这样封闭范围和局部范围都可以访问它,而不会将其视为局部分配。在回调
函数上设置属性是一种很好的方法:
def get_audio_callback(pt):
def callback(in_data, frame_count, time_info, status):
"""
This is the callback function for sound API
In each call, synthesized data is dumpped into the sound buffer
"""
wave = np.ndarray((frame_count, 2))
for i in range(frame_count):
ind = (i+callback.pt) % n
wave[i,0] = float(x[ind]) * 2
wave[i,1] = float(y[ind]) * 2
callback.pt = callback.pt + frame_count
return (encode(wave), pyaudio.paContinue)
callback.pt = pt
return callback
这里的callback.pt
不再是本地名称;它是回调
函数对象上的一个属性。我无法重现您的问题。什么是完整的回溯?你可能过于简化了。callback()
嵌套函数中的任何其他代码是否以某种方式触及了pt
?在分配pt=pt+frame\u count
时,是否在全局级别更改pt
?还是应该在get\u audio\u callback
级别进行更新?更新pt
似乎没有什么意义,否则,您没有对它做任何事情。这只是意味着您在回调的其他地方也在用pt
做其他事情。你的工作可能是个好主意,也可能不是个好主意,但是你没有给我们足够的背景来帮助我们。请看问题中我的更新。我也很好奇为什么会出现这个问题,为什么我的解决方法会有帮助。这是Python2还是Python3?你回答得很好。我的解决方法之所以有效,是因为pt=pt_ref[0]
实际上从闭包中获取了值。不管它是pt=pt_ref
还是pt=pt_ref[0]
;但是如果希望pt
充当闭包(在调用callback
之间持久化),则“从闭包分配局部变量”将不起作用,因为闭包值永远不会更新。但是,分配的局部变量pt
确实是持久化的。我不是python方面的专家,在你提到它之前我还没有意识到它很奇怪。你在任何时候都会重新分配到pt\u ref[0]
?因为您使用的是pt_ref
列表作为闭包(不要直接分配给pt_ref
名称本身,只分配给其索引),与我使用函数属性的方式相同。我恐怕不能重新分配。您可以在中看到完整的代码