Lambdify 适用于 Python,但使用 Cython 会抛出异常
Lambdify works with Python, but throws an exception with Cython
我的网站运行此 Python 脚本,如果使用 Cython,该脚本将更加优化。最近我需要添加 Sympy with Lambdify,这在 Cython 中并不顺利。
所以我将问题简化为一个最小的工作示例。在代码中,我有一个字典,其中包含字符串键和列表值。我想将这些键用作变量。在下面的简化示例中,只有 1 个变量,但通常我需要更多。请检查以下示例:
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'
所以问题是:如何让 lambdify 与 Cython 一起工作?
注意: 我想指出我有 Debian Jessie,这就是我使用 Python 3.4 的原因。另外我想指出,在不使用 lambdify
时,我对 Cython 没有任何问题。另外我想指出,Cython 已更新为 pip3 install cython --upgrade
.
的最新版本
Stack frames
Currently we generate fake tracebacks as part of exception
propagation, but don’t fill in locals and can’t fill in co_code. To be
fully compatible, we would have to generate these stack frame objects
at function call time (with a potential performance penalty). We may
have an option to enable this for debugging.
f_locals
在
AttributeError: 'NoneType' object has no attribute 'f_locals'
似乎指向了这个不兼容问题。
这是解决实际问题(在评论和@jjhakala 的回答中确定)的一种解决方法,Cython 不会为编译函数生成完整的 tracebacks/introspection 信息。我从您的评论中了解到,出于速度原因,您希望保留大部分程序使用 Cython 编译。
"solution"是只对需要调用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_module.f(g)
给了我一个键为 func_to_call
的字典(正如预期的那样),而 cython_module.f2(g)
给了我 __main__
模块全局变量(但这是因为我运行 来自口译员而不是使用 --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
.
中
我的网站运行此 Python 脚本,如果使用 Cython,该脚本将更加优化。最近我需要添加 Sympy with Lambdify,这在 Cython 中并不顺利。
所以我将问题简化为一个最小的工作示例。在代码中,我有一个字典,其中包含字符串键和列表值。我想将这些键用作变量。在下面的简化示例中,只有 1 个变量,但通常我需要更多。请检查以下示例:
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'
所以问题是:如何让 lambdify 与 Cython 一起工作?
注意: 我想指出我有 Debian Jessie,这就是我使用 Python 3.4 的原因。另外我想指出,在不使用 lambdify
时,我对 Cython 没有任何问题。另外我想指出,Cython 已更新为 pip3 install cython --upgrade
.
Stack frames
Currently we generate fake tracebacks as part of exception propagation, but don’t fill in locals and can’t fill in co_code. To be fully compatible, we would have to generate these stack frame objects at function call time (with a potential performance penalty). We may have an option to enable this for debugging.
f_locals
在
AttributeError: 'NoneType' object has no attribute 'f_locals'
似乎指向了这个不兼容问题。
这是解决实际问题(在评论和@jjhakala 的回答中确定)的一种解决方法,Cython 不会为编译函数生成完整的 tracebacks/introspection 信息。我从您的评论中了解到,出于速度原因,您希望保留大部分程序使用 Cython 编译。
"solution"是只对需要调用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_module.f(g)
给了我一个键为 func_to_call
的字典(正如预期的那样),而 cython_module.f2(g)
给了我 __main__
模块全局变量(但这是因为我运行 来自口译员而不是使用 --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
.