You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python/Kivy循环传参异常:点击标签始终传入最后一个ID

问题排查与解决

这是典型的Python闭包延迟绑定导致的问题,我来帮你拆解原因和修复方案:

问题根源

在abc()方法的循环中,你使用了lambda表达式作为on_ref_press的回调,但Python的闭包并不会在定义lambda时立刻捕获row[0]的当前值——它只会在回调实际被触发(也就是你点击标签)时,才去查找row变量的当前指向。而当循环结束后,row已经指向了数组的最后一个元素(13, 'Item3', '102'),所以无论点击哪个标签,最终传入say_hello的都是最后一个ID 13。

修复方案

这里有两种简单有效的解决办法:

方案1:给Lambda添加默认参数

利用Python默认参数在函数定义时求值的特性,把当前的row[0]值提前绑定到lambda中:

layout.add_widget(MyLabel(
    text=str('[ref=world]'+row[1])+'[/ref]',
    size_hint_x=.35, 
    halign='left', 
    markup=True,
    on_ref_press=lambda *args, id=row[0]: self.say_hello(id) 
))

方案2:使用functools.partial包装函数

通过partial提前把row[0]作为参数绑定到say_hello函数上,避免闭包延迟绑定的问题:
首先需要导入functools:

from functools import partial

然后修改循环中的回调部分:

layout.add_widget(MyLabel(
    text=str('[ref=world]'+row[1])+'[/ref]',
    size_hint_x=.35, 
    halign='left', 
    markup=True,
    on_ref_press=partial(self.say_hello, row[0]) 
))

修改后的完整test.py代码(以方案1为例)

from kivy.uix.label import Label
from kivy.uix.screenmanager import Screen
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window

Window.clearcolor = (0.5, 0.5, 0.5, 1)
Window.size = (600, 600)

class MyLabel(Label):
    pass

class Invoice(Screen):
    def __init__(self, **kwargs):
        super(Invoice, self).__init__(**kwargs)

    def abc(self):
        #fetching from database
        arr = [(11, 'Item1', '1001'), (12, 'Item2', '2001'), (13, 'Item3', '102')]
        layout = self.ids['invoices']
        # 先清空之前的控件,避免重复添加
        layout.clear_widgets()
        for row in arr:
            layout.add_widget(MyLabel(
                text=str('[ref=world]'+row[1])+'[/ref]',
                size_hint_x=.35, 
                halign='left', 
                markup=True,
                on_ref_press=lambda *args, id=row[0]: self.say_hello(id) 
            ))
            layout.add_widget(MyLabel(
                text=str(row[2]),
                size_hint_x=.15, 
                halign='right' 
            ))

    def say_hello(self, id='Zero'):
        print(id)

class Test(App):
    def build(self):
        return self.root

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

注:额外添加了layout.clear_widgets(),避免多次点击Show按钮重复添加控件。

现在点击不同的标签(Item1/Item2/Item3),控制台会分别输出对应的ID 11、12、13,问题就解决啦!

内容的提问来源于stack exchange,提问作者Nirdesh Kumawat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:16:31