Python: передача функции-члена класса в обратный вызов другого класса

Могу ли я передать класс A в класс B, чтобы B мог выполнить обратный вызов, используя функцию-член A?

Я пытаюсь написать класс ног Python для робота, которого я строю. Я использую Raspberry Pi в качестве основного компьютера и библиотеку поворотного кодера KY040 Мартина О'Хэнлона KY040 для определения каждого 1/4 поворота ноги. С этой целью я наблюдаю за первым из нескольких щелчков, на короткое время сплю, останавливаю сервопривод, и теперь достигается вращение на 1/4. В автономном коде без резьбы это работает нормально, но создание класса было проблемой.

Спасибо за ваше время.

Детали: резьбовая петля стража следит за логическим значением (quarterTurn), чтобы сигнализировать о необходимости вращения.

def run(self):
    print "leg running"
    while self._running:
        sleep(.0001)
        if self.quarterTurn:
            print "quarterTurn is: " + str(self.quarterTurn)
            self.qTurn(self.quarterCount)

qTurn обращается к контроллеру ШИМ, чтобы активировать двигатели, и сбрасывает четверть-оборот в значение false.

def qTurn(self, quarters):
    count = 0

    while count < quarters:
        sleep(.0001)
        self.setMotor(self.maxPulse)

        if self.ClickedOnce:
            count = count + 1
            sleep(.17)
            self.parkMotor()
            sleep(.04)
            self.clickedOnce = False

    self.quarterTurn = False

Хитрость в том, что класс О'Хэнлона уже нарезан. С одной стороны, это удобно, с другой - делает мой класс более сложным. KY040 использует функцию обратного вызова для обеспечения обратной связи, но использование этого в моем классе является источником моей проблемы.

Мне нужен обратный вызов для изменения логического значения в моем классе ноги, но эта функция вызывается только классом KY040, который пытается передать себя в функцию.

def rotaryChange(self, pin):
    self.clickedOnce = True

Так как код с открытым исходным кодом (спасибо, О'Хэнлон), я подумал, что мог бы изменить конструктор KY040, чтобы я мог передать ему свой класс ноги, чтобы я мог изменить правильные данные.

Оригинальный конструктор О'Хэнлона:

def __init__(self, clockPin, dataPin, switchPin=None, rotaryCallback=None, switchCallback=None,rotaryBouncetime=250, switchBouncetime=300):
    # persist values
    self.clockPin = clockPin
    self.dataPin = dataPin
    self.switchPin = switchPin
    self.rotaryCallback = rotaryCallback
    self.switchCallback = switchCallback
    self.rotaryBouncetime = rotaryBouncetime
    self.switchBouncetime = switchBouncetime

    #setup pins
    GPIO.setup(clockPin, GPIO.IN)
    GPIO.setup(dataPin, GPIO.IN)

    if None != self.switchPin:
        GPIO.setup(switchPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

Я добавил переменную "хост", в которую я передаю класс ноги:

def __init__(self, clockPin, dataPin, switchPin=None, rotaryCallback=None, switchCallback=None, host=None, rotaryBouncetime=250, switchBouncetime=300):
    # persist values
    self.clockPin = clockPin
    self.dataPin = dataPin
    self.switchPin = switchPin
    self.rotaryCallback = rotaryCallback
    self.switchCallback = switchCallback
    self.rotaryBouncetime = rotaryBouncetime
    self.switchBouncetime = switchBouncetime

    # My Change
    self.host = host

    #setup pins
    GPIO.setup(clockPin, GPIO.IN)
    GPIO.setup(dataPin, GPIO.IN)

    if None != self.switchPin:
        GPIO.setup(switchPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

Модифицированный конструктор будет называться так:

self.encoder = KY040(self.clockPin, self.dataPin, rotaryCallback=self.rotaryChange, host=self)

Обратный вызов О'Хэнлона теперь передает хосту:

def _clockCallback(self, pin):
        # My change
        self.rotaryCallback(pin, self.host)

Мой новый обратный звонок:

def rotaryChange(pin, host):
    host.clickedOnce = True

К сожалению, после проверки того, что модифицированный код установлен вместе со сценарием установки, он, похоже, не подтверждает мои новые дополнения. Я запускаю свою программу и получаю следующую ошибку:


    Traceback (most recent call last):
    File "ctf.py", line 18, in 
      LR = leg.leg(lr_chan, lr_max, lr_park, lr_clk, lr_data);
    File "/home/[user]/hexacrescentapod/leg.py", line 47, in __init__
      self.encoder = KY040(self.clockPin, self.dataPin, 
    rotaryCallback=self.rotaryChange, host=self)
    TypeError: __init__() got an unexpected keyword argument 'host'

Спасибо, что уделили время на этот длинный вопрос.

2 ответа

Решение

Это немного сбивает с толку из-за вашей формулировки. Вы на самом деле пытаетесь передать класс, как вы говорите, или экземпляр этого класса, как вы, кажется, делаете? Какой класс rotaryChange определяется в?

Во всяком случае, похоже, что вы на самом деле пытаетесь сделать, это пройти self.rotaryChange в качестве обратного вызова.

Это уже работает, без каких-либо изменений. self.rotaryChange это связанный метод, то есть он знает, что это self был, когда он был создан, и пройдет его, когда он будет вызван. Это может быть легче увидеть на примере:

>>> class Spam:
...     def eggs(self):
...         pass
>>> spam = Spam()
>>> spam
<__main__.Spam at 0x119947630>
>>> spam.eggs
<bound method Spam.eggs of <__main__.Spam object at 0x119947630>>

Обратите внимание, что это связанный метод spam объект. Когда вы звоните spam.eggs(), тот spam объект будет передан как self аргумент.

Это означает, что вам не нужно передавать host в, потому что он уже доступен как self, И, так как это единственное, что вы делаете с host вам не нужно проходить host на первом месте. Это означает, что вы можете отменить все свои изменения в коде библиотеки.

Вам нужно определить свой метод обратного вызова как правильный метод, с self в качестве первого аргумента. Но это все. Тогда вы можете просто пройти rotaryCallback=self.rotaryChange конструктору, и все будет работать.

На первый взгляд, похоже, что ваш новый обратный вызов отсутствует self поле?

Первоначальная функция была

def rotaryChange(self, pin):
    self.clickedOnce = True

Но ваша реализация:

def rotaryChange(pin, host):
    host.clickedOnce = True

Если эта функция находится внутри класса, она должна иметь self параметр

Другие вопросы по тегам