Python closure, local variable scope error

SolessChong

My function throw me with the local variable 'pt' referenced before assignment error:

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. 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))
Martijn Pieters

Your code assigns to pt in callback; Python determines the scope of a name at compile time and assignment makes this a local name.

pt = pt + frame_count

Unless you tell Python otherwise, that is. 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:

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.

In Python 2, you can just create a local that takes its value from the closure:

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.

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.

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:

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; it is an attribute on the callback function object instead.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

In Swift, what is the scope of local variable if it is used inside a closure?

Variable scope in function closure?

Variable Scope in Closure

Go closure variable scope

Understanding closure scope in Python

C# closure variable scope

How to force Python to report an error if a local variable is referenced out of its scope?

UnboundLocalError: local variable referenced before assignment in python closure

Constructing functions with multiple methods inside a closure, and the error "syntax: local variable T cannot be used in closure declaration"

Initialize local variable inside closure

Error:A local variable named 'Model' cannot be declared in this scope

trying to declare a variable local to a function, but get scope error

Compilation error: Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure

How to access variable from Closure Scope in JavaScript

swift variable scope in closure wrt CLGeocoder()

Groovy 2.4 variable scope in closure with @Field annotation

Why isn't Python switching from enclosing to local variable scope?

Python scope: "UnboundLocalError: local variable 'c' referenced before assignment"

Closure and variable scope in python 3.x issue causing me confusion

Why does moving a value into a closure still have the error message "cannot borrow immutable local variable as mutable"?

define variable of global scope in local scope

Scope of local variables in Python

Python: exec in local scope

How to declare a local variable that is a closure in swift

Log related local variable in python on error

Python error -- local variable reference before assignment

scope of local variable in enum constant

Instance variable with local method scope

Lua - Local variable scope in function