我有一个Python脚本,当我正常运行它时,它可以很好地运行:
$ python script.py <options>
我正在尝试使用cProfile模块来分析代码:
$ python -m cProfile -o script.prof script.py <options>
当我启动上述命令时,我收到一个关于无法使函数腌制的错误:
Traceback (most recent call last):
File "scripts/process_grid.py", line 1500, in <module>
_compute_write_index(kwrgs)
File "scripts/process_grid.py", line 626, in _compute_write_index
args,
File "scripts/process_grid.py", line 1034, in _parallel_process
pool.map(_apply_along_axis_palmers, chunk_params)
File "/home/james/miniconda3/envs/climate/lib/python3.6/multiprocessing/pool.py", line 266, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/home/james/miniconda3/envs/climate/lib/python3.6/multiprocessing/pool.py", line 644, in get
raise self._value
File "/home/james/miniconda3/envs/climate/lib/python3.6/multiprocessing/pool.py", line 424, in _handle_tasks
put(task)
File "/home/james/miniconda3/envs/climate/lib/python3.6/multiprocessing/connection.py", line 206, in send
self._send_bytes(_ForkingPickler.dumps(obj))
File "/home/james/miniconda3/envs/climate/lib/python3.6/multiprocessing/reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <function _apply_along_axis_palmers at 0x7fe05a540b70>: attribute lookup _apply_along_axis_palmers on __main__ failed
代码使用多重处理,我认为这是进行酸洗的地方。
正在播放的代码为here on GitHub。
基本上,我在进程池中映射了一个函数和一个对应的参数字典:
pool.map(_apply_along_axis_palmers, chunk_params)
据我所知,函数_apply_along_axis_palmers
是“可拾取的”,因为它是在模块的顶层定义的。再次在cProfile上下文之外运行时不会发生此错误,是否可能为酸洗添加了其他约束?
任何人都可以评论为什么会发生这种情况,和/或如何纠正该问题吗?
预先感谢您的任何建议。
答案 0 :(得分:3)
您遇到的问题是,通过使用-mcProfile
,模块__main__
是cProfile
(代码的实际入口点),而不是脚本。 cProfile
尝试来解决此问题,方法是确保脚本在运行时将__name__
视为"__main__"
,因此它知道它是作为脚本运行的,而不是导入的作为一个模块,但是sys.modules['__main__']
仍然是cProfile
模块。
问题是,pickle
仅通过腌制其合格名称来处理腌制功能(加上一些样板代码可以说是一开始的功能)。为了确保在往返过程中仍然生存,它始终会仔细检查可在sys.modules
中查找合格名称。因此,当您执行pickle.dumps(_apply_along_axis_palmers)
(显式或隐式地通过将其作为映射器函数传递)时,在主脚本中定义了_apply_along_axis_palmers
时,它将再次检查sys.modules['__main__']._apply_along_axis_palmers
是否存在。但这不存在,因为cProfile._apply_along_axis_palmers
不存在。
我不知道有什么 good 解决方案。我能想到的最好的办法是手动修复sys.modules
,以正确公开您的模块及其内容。我尚未对此进行完整的测试,因此可能会有一些古怪之处,但我发现的解决方案是更改名为mymodule.py
的模块,其形式为:
# imports...
# function/class/global defs...
if __name__ == '__main__':
main() # Or series of statements
收件人:
# imports...
import sys
# function/class/global defs...
if __name__ == '__main__':
import cProfile
# if check avoids hackery when not profiling
# Optional; hackery *seems* to work fine even when not profiling, it's just wasteful
if sys.modules['__main__'].__file__ == cProfile.__file__:
import mymodule # Imports you again (does *not* use cache or execute as __main__)
globals().update(vars(mymodule)) # Replaces current contents with newly imported stuff
sys.modules['__main__'] = mymodule # Ensures pickle lookups on __main__ find matching version
main() # Or series of statements
从此开始,sys.modules['__main__']
指的是您自己的模块,而不是cProfile
,因此一切似乎正常。尽管如此,cProfile
似乎仍然可以工作,酸洗可以按预期找到您的功能。重新导入模块只有实际成本,但是如果您做的是足够的实际工作,则重新导入的成本应该很小。