【问题标题】:SOLVED - Python: Passing a class member function to another class's callback已解决 - Python:将类成员函数传递给另一个类的回调
【发布时间】:2018-04-04 04:14:02
【问题描述】:

我可以将 A 类传递给 B 类,以便 B 可以使用 A 的成员函数运行回调吗?

我正在尝试为我正在构建的机器人编写 Python 腿类。我使用 Raspberry Pi 作为主计算机,使用 Martin O'Hanlon 的 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 访问 pwm 控制器以激活电机,并将 QuarterTurn 重置为 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

诀窍在于 O'Hanlon 的类已经线程化。一方面,它很方便,另一方面,它使我的课更复杂。 KY040 使用回调函数来提供反馈,但在我的课堂上使用它是我的麻烦之源。

我需要回调来修改我的腿类中的布尔值,但是这个函数只被 KY040 类调用,它试图将自己传递给函数。

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

由于代码是开源的(谢谢,O'Hanlon),我想我可以修改 KY040 的构造函数,让我将我的腿类传递给它,这样我就可以修改正确的数据。

O'Hanlon 的原始构造函数:

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)

O'Hanlon 的回调现在传递主机:

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'

感谢您花时间回答这个冗长的问题。

【问题讨论】:

    标签: python callback raspberry-pi robot


    【解决方案1】:

    由于您的措辞,这有点令人困惑。您实际上是在尝试像您所说的那样传递一个 class,还是像您似乎在做的那样传递该类的 instancerotaryChange 定义在哪个类中?

    无论如何,看起来您实际上想要做的是将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 传递给构造函数,一切都会正常工作。

    【讨论】:

    • 这很有趣。我会试一试,然后告诉你会发生什么。
    • @GageGeigle 大多数 Python 教程都没有介绍绑定方法,也许是因为它们看起来有点吓人,但它们非常有用——非常有用,以至于教程经常最终使用它们,即使它们从未解释过.可能有帮助的一件事是考虑您可以编写一个仅执行 return spam.eggs() 的函数并将其传递,将其存储在变量中并稍后调用它,无论如何,然后您可以通过传递 @ 来做同样的事情987654337@ 本身,并确保没有任何区别(名称和速度除外)。
    【解决方案2】:

    乍一看,您的新回调似乎缺少 self 字段?

    原来的功能是

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

    但你的实现是:

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

    如果这个函数位于一个类中,它需要有一个self 参数

    【讨论】:

    • 确实,我也认为传递self 是对类成员的要求,但更改为第二个实现是我能想到的唯一方法,以防止 KY040 类将自身传递到 @ 987654326@参数。
    • @GageGeigle 这也是 Python 使用显式 self 的部分原因,因此始终清楚传递了谁的 self。如果KY040 中的代码没有调用self.something,其中something 是类或子类的方法名称,则它不会传递自身。但是,存储在实例变量中的可调用对象有时会让人感到困惑。您必须注意 self.callback 被分配在 __init__ 或其他一些方法体中,以知道它不是一个方法,因此不会传递自己的 self
    • @GageGeigle 当您不确定时,通常值得构建一个具有相同模式的玩具类,但没有其他代码,因此您可以运行它并打印出调用的内容.
    • @abarnert 非常感谢。我会这样做的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    相关资源
    最近更新 更多