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
相关产品推荐
相关产品推荐

