使用pythonnet与nicegui时遇无法pickle Decimal对象问题求解决
解决NiceGUI中run.cpu_bound无法序列化Python.NET Decimal对象的问题
问题场景
在NiceGUI中通过按钮触发创建包含System.Decimal属性的类(依赖pythonnet),使用官方示例的上下文管理器实现按钮禁用时,调用run.cpu_bound会抛出TypeError: cannot pickle 'Decimal' object错误。移除Decimal属性或不使用cpu_bound时代码可正常运行,但业务需要保留Decimal类型以传递给外部DLL。
报错原因
NiceGUI的run.cpu_bound基于多进程实现,进程间通信要求返回值能被Python标准库的pickle序列化。但pythonnet封装的System.Decimal类型不属于Python原生可序列化类型,无法被pickle处理,因此触发错误。
解决方案
根据业务场景选择以下两种可行方案:
方案1:改用run.io_bound(推荐,适合调用外部DLL场景)
如果你的耗时操作是调用外部DLL(属于IO密集型任务),直接将run.cpu_bound替换为run.io_bound即可。run.io_bound基于线程实现,线程间共享内存空间,不需要序列化返回值,因此可以正常传递包含System.Decimal的对象。
修改后的核心代码:
async def get_slow_response(button: ui.button) -> None: with disable(button): # 替换cpu_bound为io_bound return_value = await run.io_bound(some_function)
方案2:在子进程内完成DLL交互,仅返回可序列化结果
如果必须使用多进程(严格CPU密集型任务),则在cpu_bound调用的函数内完成与DLL的全部交互,不返回包含System.Decimal的对象,仅返回字符串、数值等原生可序列化类型的结果。
修改后的示例代码:
def some_function(): obj = my_class(1) # 在这里完成与外部DLL的交互逻辑 # 仅返回可序列化的结果,比如Decimal的字符串表示 return str(obj.value) async def get_slow_response(button: ui.button) -> None: with disable(button): return_value = await run.cpu_bound(some_function) # 后续用return_value更新UI ui.notify(f"处理结果:{return_value}")
完整可运行代码(方案1示例)
from contextlib import contextmanager from nicegui import run, ui import clr from System import Decimal @contextmanager def disable(button: ui.button): button.disable() try: yield finally: button.enable() class my_class: def __init__(self, value): self.value = Decimal(value) def some_function(): return my_class(1) async def get_slow_response(button: ui.button) -> None: with disable(button): return_value = await run.io_bound(some_function) # 验证返回的对象及Decimal属性 ui.notify(f"创建的对象值:{return_value.value}") ui.button('Get slow response', on_click=lambda e: get_slow_response(e.sender)) ui.run(port=80)
内容的提问来源于stack exchange,提问作者Omnistic
相关产品推荐
相关产品推荐

