Python闭包,局部变量范围错误

时间:2014-04-19 13:38:01

标签: python closures

我的函数抛出了local variable 'pt' referenced before assignment错误:

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

2 个答案:

答案 0 :(得分:2)

您的代码分配给pt中的callback ; Python在编译时确定名称的范围,赋值使其成为本地名称。

pt = pt + frame_count

除非你告诉Python,否则就是这样。在Python 2中,您只能将名称明确标记为global,而您需要Python 3才能使用nonlocal关键字:

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_audio_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_callback进行更新(例如,callback被多次调用,您需要pt从呼叫更新到pt (请致电),您需要完全避免在callback功能中使用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不再是本地名称;它是{{1}}函数对象的一个​​属性。

答案 1 :(得分:0)

原来是'参考'的问题

我将我的代码更改为按变量传递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