Lambdify与Python一起工作,但与Cython一起引发异常
我的网站运行这个Python脚本,如果使用Cython,这个脚本将更加优化。最近我需要补充一点,Cython的情况不太好 所以我把这个问题简化成一个最简单的例子。在代码中,我有一个带有字符串键的字典,其中的值是列表。我想使用这些键作为变量。在下面的简化示例中,只有1个变量,但通常我需要更多。请检查以下示例:Lambdify与Python一起工作,但与Cython一起引发异常,python,optimization,cython,sympy,lambdify,Python,Optimization,Cython,Sympy,Lambdify,我的网站运行这个Python脚本,如果使用Cython,这个脚本将更加优化。最近我需要补充一点,Cython的情况不太好 所以我把这个问题简化成一个最简单的例子。在代码中,我有一个带有字符串键的字典,其中的值是列表。我想使用这些键作为变量。在下面的简化示例中,只有1个变量,但通常我需要更多。请检查以下示例: import numpy as np from sympy.parsing.sympy_parser import parse_expr from sympy.utilities.lambd
import numpy as np
from sympy.parsing.sympy_parser import parse_expr
from sympy.utilities.lambdify import lambdify, implemented_function
from sympy import S, Symbol
from sympy.utilities.autowrap import ufuncify
def CreateMagneticFieldsList(dataToSave,equationString,DSList):
expression = S(equationString)
numOfElements = len(dataToSave["MagneticFields"])
#initialize the magnetic field output array
magFieldsArray = np.empty(numOfElements)
magFieldsArray[:] = np.NaN
lam_f = lambdify(tuple(DSList),expression,modules='numpy')
try:
# pass
for i in range(numOfElements):
replacementList = np.zeros(len(DSList))
for j in range(len(DSList)):
replacementList[j] = dataToSave[DSList[j]][i]
try:
val = np.double(lam_f(*replacementList))
except:
val = np.nan
magFieldsArray[i] = val
except:
print("Error while evaluating the magnetic field expression")
return magFieldsArray
list={"MagneticFields":[1,2,3,4,5]}
out=CreateMagneticFieldsList(list,"MagneticFields*5.1",["MagneticFields"])
print(out)
让我们称之为test.py
。这很有效。现在我想对此进行cythonize,因此我使用以下脚本:
#!/bin/bash
cython --embed -o test.c test.py
gcc -pthread -fPIC -fwrapv -Ofast -Wall -L/lib/x86_64-linux-gnu/ -lpython3.4m -I/usr/include/python3.4 -o test.exe test.c
现在,如果我执行/test.exe
,它会抛出一个异常!例外情况如下:
Traceback (most recent call last):
File "test.py", line 42, in init test (test.c:1811)
out=CreateMagneticFieldsList(list,"MagneticFields*5.1",["MagneticFields"])
File "test.py", line 19, in test.CreateMagneticFieldsList (test.c:1036)
lam_f = lambdify(tuple(DSList),expression,modules='numpy')
File "/usr/local/lib/python3.4/dist-packages/sympy/utilities/lambdify.py", line 363, in lambdify
callers_local_vars = inspect.currentframe().f_back.f_locals.items()
AttributeError: 'NoneType' object has no attribute 'f_locals'
所以问题是:我怎样才能让兰比菲与赛顿合作
注意:我想指出我有Debian Jessie,这就是我使用Python 3.4的原因。另外,我想指出,当我不使用lambdify
时,我对Cython没有任何问题。我还想指出,Cython已通过pip3安装Cython更新到最新版本--升级
中有说明
堆叠框架
目前,作为异常的一部分,我们生成假回溯
传播,但不要填写局部变量,也不能填写co_代码。成为
完全兼容,我们必须生成这些堆栈帧对象
在函数调用时(具有潜在的性能损失)。我们可以
有一个选项可以启用此选项进行调试
f_locals
in
AttributeError: 'NoneType' object has no attribute 'f_locals'
似乎指向了这个不兼容问题。这是一个解决实际问题的方法(在注释和@jhakala的回答中确定),Cython没有为编译函数生成完整的回溯/内省信息。我从您的评论中得知,出于速度原因,您希望使用Cython编译大部分程序 “解决方案”是只对需要调用
lambdify
的单个函数使用Python解释器,而将其余函数保留在Cython中。您可以使用exec
执行此操作
这个想法的一个非常简单的例子是
exec("""
def f(func_to_call):
return func_to_call()""")
# a Cython compiled version
def f2(func_to_call):
return func_to_call())
可以将其编译为Cython模块并导入,导入后Python解释器将运行字符串中的代码,并正确地将f
添加到模块全局。如果我们创建一个纯Python函数
def g():
return inspect.currentframe().f_back.f_locals
调用cython\u module.f(g)
会给我一个带有键func\u to\u call
(如预期)的字典,而cython\u module.f2(g)
会给我\u main\u
模块全局(但这是因为我是从解释器运行的,而不是使用--embed
)
编辑:基于代码的完整示例
from sympy import S, lambdify # I'm assuming "S" comes from sympy
import numpy as np
CreateMagneticFieldsList = None # stops a compile error about CreateMagneticFieldsList being undefined
exec("""def CreateMagneticFieldsList(dataToSave,equationString,DSList):
expression = S(equationString)
numOfElements = len(dataToSave["MagneticFields"])
#initialize the magnetic field output array
magFieldsArray = np.empty(numOfElements)
magFieldsArray[:] = np.NaN
lam_f = lambdify(tuple(DSList),expression,modules='numpy')
try:
# pass
for i in range(numOfElements):
replacementList = np.zeros(len(DSList))
for j in range(len(DSList)):
replacementList[j] = dataToSave[DSList[j]][i]
try:
val = np.double(lam_f(*replacementList))
except:
val = np.nan
magFieldsArray[i] = val
except:
print("Error while evaluating the magnetic field expression")
return magFieldsArray""")
list={"MagneticFields":[1,2,3,4,5]}
out=CreateMagneticFieldsList(list,"MagneticFields*5.1",["MagneticFields"])
print(out)
当使用脚本编译时,将打印此文件
[5.1 10.2 15.3 20.4 25.5]
实际上,我所做的只是将函数包装在一个
exec
语句中,因此它由Python解释器执行。这部分不会从Cython中看到任何好处,但是程序的其余部分仍然会看到。如果你想最大限度地利用Cython编译,你可以把它分成多个函数,这样只有包含lambdify
的一小部分在exec
中相关:从我看来,--embed
不是魔法,也不会进行优化(它只是从libpython
调用解释器),所以没什么必要这么做。目的是什么?@ivan_pozdeev嗯,它比原始蟒蛇快多了。。。您是否建议删除--embed
?可能相关,“目前,作为异常传播的一部分,我们生成假回溯,但不要填写局部变量,也不能填写co_代码。”@J.J.Hakala将此作为一个答案发布,因为很可能是这样。您可以修改您的代码以与我提供的参数兼容吗?我对这个很陌生。我试着把这些作为参数放到f
中,但我得到了一个错误:fdata=f(CreateMagneticFieldList,all_data,feqStr,all_symbols)TypeError:“\u io.TextIOWrapper”对象不可调用Ah。我想你有点误解了。您需要在eval
内部定义createMagneticFieldList
,因为它是调用lambdify
的。我只是举了一个非常简单的例子来说明这个想法是有效的<正如我定义的那样,code>f
真的毫无意义。(如果这仍然让人困惑的话,我会尽量使我的代码更加完整)。我非常非常感谢一个实用的例子,最好是基于我在问题中提供的例子!我也向你保证,每个人都会喜欢它,因为这个问题很常见,没有人找到解决办法。