按钮上的绑定在 Kivy 中令人困惑

The binding on button is confusing in Kivy

作为测试,我准备了以下代码,

首先,我从两个函数名称 add_frontadd_back

设置按钮文本

然后,我从名称中获取函数句柄,并创建一个部分函数将其绑定到 Buttons。

虽然绑定看起来没问题,但结果是随机的。

有人能帮帮我吗?

"""
@author:
@file:test-bind.py
@time:2022/01/3111:42
@file_desc
"""
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty
from kivy.clock import Clock
import random

from functools import partial

Builder.load_string("""
<MButton@Button>:
    _cb:[]
    
<TestWin>:
    inp1:inp1_
    lbl1:lbl1_
    btn1:btn1_
    btn2:btn2_    
    orientation: 'vertical'
    BoxLayout:
        orientation:'horizontal'
        TextInput:
            id:inp1_
            readonly: True
        Label:
            id:lbl1_
    MButton:
        id:btn1_
        text: 'add_front'
    MButton:
        id:btn2_
        text: 'add_back'
    Button:
        id:btn_cls_
        text:'clear'
        on_press:root.clear_elements()
    Button:
        id:btn_shuffle_
        text:'Shuffle'
        on_press:root.shuffle_btn()
    TextInput:
        multiline: True
        text:'Usage:  press <Shuffle> to randomize button function and set a random number in [0,9], press <add_front> or <add_back> buttons to insert to list'
""")

class Box:
    def __init__(self):
        self.elements =[]

    def add(self,e,front=False):
        if front:
            self.elements.insert(0,e)
        else:
            self.elements.append(e)

    def add_front(self,e):
        print("add_front",e)
        self.add(e,front=True)

    def add_back(self,e):
        print("add_back",e)
        self.add(e,front=False)

class TestWin(BoxLayout):
    inp1 = ObjectProperty()
    lbl1 = ObjectProperty()
    btn1 = ObjectProperty()
    btn2 = ObjectProperty()
    btn_bind = ObjectProperty()

    def __init__(self, **kwargs):
        super(TestWin, self).__init__(**kwargs)
        self.box = Box()
        Clock.schedule_interval(self.update_elements_display, 0.5)

    def update_elements_display(self,*args):
        self.lbl1.text = "%s"%str(self.box.elements)
        pass

    def clear_elements(self):
        self.box.elements=[]

    def shuffle_btn(self):
        btn_txt_ = ["add_front", "add_back"]
        random.shuffle(btn_txt_)
        self.btn1.text = btn_txt_[0]
        self.btn2.text = btn_txt_[1]
        v = random.randint(0,9)
        self.inp1.text= "%d"%v
        # bind func
        for btn in [self.btn1,self.btn2]:
            # clear old bind firstly
            for cb in btn._cb:
                btn.funbind("on_press",cb)
            btn._cb = []
            # The following codes give wrong result
            #foo_ = getattr(self.box, btn.text)
            #foo = lambda elem, instance: foo_(elem)
            #call_back_ = partial(foo, self.inp1.text)
            # The following codes give correct result
            if btn.text=="add_back":
                call_back_ = partial(self.box.add_back, self.inp1.text)
            elif btn.text =="add_front":
                call_back_ = partial(self.box.add_front, self.inp1.text)

            btn._cb.append(call_back_)
            btn.fbind('on_press',call_back_)
            print("bind to",call_back_)


class TestApp(App):
    def build(self):
        return TestWin()

if __name__ == '__main__':
    TestApp().run()

编辑: 修改以下代码可能会给我正确的结果, 但我想知道为什么

            # The following codes give wrong result
            #foo_ = getattr(self.box, btn.text)
            #foo = lambda elem, instance: foo_(elem)
            #call_back_ = partial(foo, self.inp1.text)
            # The following codes give correct result
            if btn.text=="add_back":
                call_back_ = partial(self.box.add_back, self.inp1.text)
            elif btn.text =="add_front":
                call_back_ = partial(self.box.add_front, self.inp1.text)
            # The following doesn't work
            # foo_ = getattr(self.box, btn.text)
            # foo = lambda elem, instance: foo_(elem)
            # call_back_ = partial(foo, self.inp1.text)


            #this is ok
            call_back_ = partial(getattr(self.box, btn.text), self.inp1.text)