简体   繁体   English

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

[英]Python closure, local variable scope error

My function throw me with the local variable 'pt' referenced before assignment error: 我的函数让我local variable 'pt' referenced before assignment错误local variable 'pt' referenced before assignmentlocal 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

the code is as follows 代码如下

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

and in global scope, 在全球范围内

pt = 0
stream = p.open(stream_callback=get_audio_callback(pt))

I cannot figure out why the error occurs, since I've checked with some examples on closure and find no difference. 我无法弄清楚为什么会发生错误,因为我已经检查了一些有关闭包的示例,但没有发现任何区别。

Edit 编辑

The reason why you cannot reproduce the error might because of the over-simplify, as mentioned by @Martijn Pieters. 如@Martijn Pieters所述,您之所以无法重现该错误,可能是因为过于简化了。 Hence the original code. 因此是原始代码。

Further I've solved this problem by passing by reference, plz see my own answer. 此外,我已经通过引用的方式解决了这个问题,请参见我自己的答案。

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

Your code assigns to pt in callback ; 您的代码在callback 分配给pt Python determines the scope of a name at compile time and assignment makes this a local name. Python在编译时确定名称的范围,赋值使其成为本地名称。

pt = pt + frame_count

Unless you tell Python otherwise, that is. 除非您另外告诉Python,否则就是这样。 In Python 2, you can only mark a name explicitly as a global instead, you need Python 3 to be able to use the nonlocal keyword: 在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)

With the nonlocal pt line Python is explicitly told not to treat pt as a local name but to take it from the enclosing scope of get_audio_callback instead. nonlocal pt行中,明确告知Python不要将pt视为本地名称,而get_audio_callback的封闭范围中get_audio_callback

In Python 2, you can just create a local that takes its value from the closure: 在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)

because your enclosing get_audio_callback scope doesn't appear to use pt anyway and won't need access to the updated pt_local value. 因为您get_audio_callback范围似乎仍然不使用pt 并且不需要访问更新的pt_local值。

If you do need pt to update at the get_audio_callback scope (if, say, callback is called multiple times and you need pt to be updated from call to call), you need to avoid using pt as a local inside the callback function altogether. 如果确实需要ptget_audio_callback范围内进行更新(例如,如果多次callback ,并且需要从一次调用到另一次更新pt ),则需要避免将pt用作callback函数内部的本地对象。

One effective work-around for that is to wrap the value in a mutable object or assign it as a mutable attribute somewhere that both the enclosing scope and the local scope can access it without it ever being seen as a local assignment. 一种有效的解决方法是将值包装在可变对象中,或将其分配为可变属性,以使封闭范围和本地范围都可以访问该值,而不会将其视为本地赋值。 Setting an attribute on the callback function is a good way to do that: 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

Here callback.pt is no longer a local name; 这里的callback.pt不再是本地名称; it is an attribute on the callback function object instead. 它是callback函数对象上的一个属性。

It turned out to be a problem about 'reference' 原来是关于“参考”的问题

I changed my code into passing pt by variable, and it worked out fine. 我将代码更改为通过变量传递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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM