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
名称本身,只分配给其索引),与我使用函数属性的方式相同。我恐怕不能重新分配。您可以在中看到完整的代码