Как класс может генерировать функцию обратного вызова, которая изменяется в соответствии с атрибутом класса? - PullRequest
0 голосов
/ 30 апреля 2018

У меня такая ситуация:

module1.py * * 1004

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?

Вопрос: Как изменить функцию обратного вызова audiocallback(...) в соответствии с новым значением a.liverecording? Если это работает из коробки, почему это?

В частности, f, созданный с помощью f = a.getaudiocallback(), сохраняет в своем коде указатель на a.liverecording (поэтому, если последний будет изменен, это будет учтено), или копия значения a.liverecording (т.е. False) на момент создания f?

1 Ответ

0 голосов
/ 30 апреля 2018

Если вы понимаете замыкания, единственная хитрость здесь в том, что локальная переменная, которую вы захватываете в замыкании, это параметр self внутри getaudiocallback.

Внутри этого метода self - это, конечно, AudioEngine экземпляр a. Итак, значение перехваченной вами переменной - это тот же экземпляр.

Фактически, Python позволяет вам размышлять практически обо всем во время выполнения, так что вы можете увидеть это непосредственно:

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

Если бы getaudiocallback был все еще действующим, и он восстановился бы self к некоторому другому значению, это f.__closure__[0] обновилось бы, чтобы указать на новое значение self. Так как это уже вышло, этого никогда не случится; ячейка всегда будет указывать на экземпляр, который был в a во время вызова метода.

Но если этот экземпляр позже видоизменится, как, например, когда вы пишете a.liverecording = True, вы, конечно, можете это увидеть.

...