将lambda函数传入threading.Thread时的结果异常问题排查与修复
Python多线程中Lambda捕获变量导致重复值问题及解决方法
我是Python新手,代码若未遵循最佳实践请见谅,也欢迎大家提出Pythonic的改进建议。
我编写了如下代码,期望根据名称输出对应问候语,但实际输出两次都显示名称Sal:
from time import sleep import threading def printGreating(get_greeting): sleep(5) greeting = get_greeting() print(greeting) def createGreeting(greeting,name): return greeting+' '+ name nameslist = ["Sam","Sal"] threads = [] for n in nameslist: print(n) if n == "Sam": create_greeting_l = lambda : createGreeting("Hello",n) else: create_greeting_l = lambda : createGreeting("Greeting",n) t = threading.Thread(target=printGreating, args=(create_greeting_l,)) t.start() threads.append(t) for t in threads: t.join() print('-------------------Completed--------------')
期望输出:
Sam Sal Hello Sam Greeting Sal -------------------Completed--------------
实际输出:
Sam Sal Greeting Sal Hello Sal
问题原因
Lambda表达式在循环中捕获变量n时,不会立即绑定当前迭代的n值,而是在Lambda被调用时(也就是线程执行get_greeting()时)才去查找n的当前值。循环结束后,n的最终值是"Sal",所以两个Lambda调用时都使用了这个值,导致输出重复。
解决方案
方法1:将名称作为参数传入Lambda(我找到的解决方法)
通过给Lambda添加参数,并在创建线程相关对象时传入当前的n值,强制绑定迭代时的变量值:
if n == "Sam": create_greeting_l = lambda a : createGreeting("Hello",a) else: create_greeting_l = lambda a : createGreeting("Greeting",a) t = GreeterClass(create_greeting_l,n)
方法2:用Lambda默认参数绑定当前值
更简洁的方式是利用Lambda的默认参数特性,在定义时就绑定当前的n值:
for n in nameslist: print(n) if n == "Sam": create_greeting_l = lambda n=n: createGreeting("Hello", n) else: create_greeting_l = lambda n=n: createGreeting("Greeting", n) t = threading.Thread(target=printGreating, args=(create_greeting_l,)) t.start() threads.append(t)
方法3:使用functools.partial(更Pythonic)
functools.partial可以直接创建带有预设参数的可调用对象,代码更清晰易读,避免Lambda闭包的陷阱:
from time import sleep import threading from functools import partial def print_greeting(get_greeting): sleep(5) greeting = get_greeting() print(greeting) def create_greeting(greeting,name): return greeting+' '+ name nameslist = ["Sam","Sal"] threads = [] for n in nameslist: print(n) if n == "Sam": get_greeting = partial(create_greeting, "Hello", n) else: get_greeting = partial(create_greeting, "Greeting", n) t = threading.Thread(target=print_greeting, args=(get_greeting,)) t.start() threads.append(t) for t in threads: t.join() print('-------------------Completed--------------')
Pythonic改进建议
- 函数名遵循PEP8规范:比如
printGreating改为print_greeting,createGreeting改为create_greeting,提升代码可读性。 - 优先使用
functools.partial替代复杂的Lambda闭包,尤其是在循环中创建可调用对象时,能避免变量捕获的问题。 - 可以考虑将线程逻辑封装成类,让代码结构更清晰(如示例中的
GreeterClass思路)。
内容的提问来源于stack exchange,提问作者samsal77
相关产品推荐
相关产品推荐

