起因是公司的项目为了安全和执行速度,在发布时会使用 Cython 转为 C 语言并编译成动态连接库进行调用,但是有个函数在 Python 执行时正常,但是在动态连接库中却执行错误。
错误复现
测试用例 test.py:
def ip_str(ips: str):
ips = [x for x in ips]
def ip(ips):
ips = [x for x in ips]
编译 compile.py:
from setuptools import setup
from Cython.Build import cythonize
extensions = ["test.py",]
setup(name='test',
ext_modules=cythonize(extensions)
)
运行编译:
python compile.py build
编译之后进入对应 so 文件目录 build/lib-{平台架构},运行对比:
>>> import test
>>> test.ip("123")
>>> test.ip_str("123")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "test.py", line 5, in test.ip_str
TypeError: Expected str, got list
最近工作中遇到关于函数类型注释引起的错误,特此记录一下。
起因是公司的项目为了安全和执行速度,在发布时会使用 Cython 转为 C 语言并编译成动态连接库进行调用,但是有个函数在 Python 执行时正常,但是在动态连接库中却执行错误。
错误复现
测试用例
test.py
:编译
compile.py
:运行编译:
编译之后进入对应 so 文件目录
build/lib-{平台架构}
,运行对比:经过调查发现,当函数变量做了类型注释时,不能重新赋值为其他类型,否则会在 Cython 编译后执行时报错。
Cython 可以在编译时推断出部分简单的错误,比如:
但如果代码比较复杂,则只能在运行时才会出错。所以上述错误只能在执行的时候才被抛出。
原因
Cython 将 Python 转为 C 代码比较后类型注释与否代码比较:
没有类型注释:
有类型注释:
主要区别在于,类型注释增加了变量检测
__Pyx_ArgTypeTest
,以及之后赋值 ips 时的类型检测PyString_CheckExact
;没有变量类型注释则进行了变量推测,判断是否为List(PyList_CheckExact
)或者Tuple (PyTuple_CheckExact
),还是可迭代类型。结论