Basj
Basj

Reputation: 46513

How can a class generate a callback function, that changes according to a class attribute?

I have this situation:

module1.py:

class AudioEngine:
    def __init__(self):
        self.liverecording = False

    def getaudiocallback(self):

        def audiocallback(in_data, frame_count, time_info, status):  # these 4 parameters are requested by pyaudio
            data = None      # normally here we process the audio data
            if self.liverecording:
                print("Recording...")
            return data

        return audiocallback

main.py:

import module1

a = module1.AudioEngine()
f = a.getaudiocallback()

f(0, 0, 0, 0)

a.liverecording = True

f(0, 0, 0, 0)  # prints "Recording...", which is the expected behaviour, but why does it work?

Question: How to make the callback function audiocallback(...) change accordingly to the new value of a.liverecording? If it works out of the box, why does it?

More specifically, does f, once created with f = a.getaudiocallback(), keep in his code a pointer to a.liverecording (so if the latter is modified, this will be taken into consideration), or a copy of the value of a.liverecording (i.e. False) at the time of the creation of f?

Upvotes: 1

Views: 529

Answers (1)

abarnert
abarnert

Reputation: 365807

If you understand closures, the only trick here is that the local variable you're capturing in your closure is the self parameter inside getaudiocallback.

Inside that method, the self is of course the AudioEngine instance a. So, the value of the variable that you've captured is that same instance.

In fact, Python lets you reflect on almost everything at runtime, so you can see this directly:

>>> f = a.getaudiocallback()
>>> f
<function __main__.AudioEngine.getaudiocallback.<locals>.audiocallback(in_data, frame_count, time_info, status)>
>>> f.__closure__[0].cell_contents
<__main__.AudioEngine at 0x11772b3c8>
>>> f.__closure__[0].cell_contents is a
True

If getaudiocallback were still live, and it rebound self to some other value, that f.__closure__[0] would update to point to the new value of self. Since it's already exited, that's never going to happen; the cell will always be pointing at the instance that was in a at the time the method was called.

But if that instance later gets mutated, as when you write a.liverecording = True, of course you can see that.

Upvotes: 2

Related Questions