【问题标题】:Cython unable to import and execute a function: Storing unsafe C derivative of temporary Python referenceCython 无法导入和执行函数:存储临时 Python 引用的不安全 C 派生
【发布时间】:2020-02-24 10:19:19
【问题描述】:

我是 Cython 的新手,只是想尝试一个简单的脚本。我无法从模块中导入函数并在另一个文件中使用它,但如果我在同一个文件中声明该函数,它就可以正常工作。这里可能是什么问题。我错过了什么吗?

这是sn-p的原始代码(test_cy.pyx):

# test_cy.pyx

import json

cpdef char* say_hello():
  message = json.dumps({"message": "Hello world"})
  return message.encode()

cdef char* fn(int n):
  cdef char* hello = say_hello()
  return hello

def test():
  cdef char* n = fn(1000)
  print(n)

我可以运行它(编译后):

>>> import test_cy
>>> test_cy.test()
b'Hello world'

但是,如果我将 say_hello 函数移动到另一个文件 (utils.pyx):

# utils.pyx

import json

cpdef char* say_hello():
  message = json.dumps({"message": "Hello world"})
  return message.encode()

... 并将其导入到我原来的 test_cy.pyx 文件中,如下所示:

# test_cy.pyx

from utils import say_hello

cdef char* fn(int n):
  cdef char* hello = say_hello()
  return hello

def test():
  cdef char* n = fn(1000)
  print(n)

我无法编译它,因为我收到以下错误:

>>> python3 setup.py build_ext --inplace
...
Error compiling Cython file:
------------------------------------------------------------
...
from utils import say_hello

cdef char* fn(int n):
  cdef char* hello = say_hello()
      ^
------------------------------------------------------------

test_cy.pyx:4:7: Storing unsafe C derivative of temporary Python reference
Traceback (most recent call last):
  File "setup.py", line 11, in <module>
    sources=["utils.pyx"]
  File "/Users/asim/.pyenv/versions/3.6.9/lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1101, in cythonize
    cythonize_one(*args)
  File "/Users/asim/.pyenv/versions/3.6.9/lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1224, in cythonize_one
    raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: test_cy.pyx

这是我的 setup.py 文件:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

setup(
  ext_modules = cythonize([
    Extension("test_cy",
              sources=["test_cy.pyx"]
    ),
    Extension("utils",
              sources=["utils.pyx"]
    )
  ])
)

请帮助我,像从另一个模块导入函数这样简单的事情不应该这么复杂。我可能会遗漏一些微不足道的东西。谢谢!

【问题讨论】:

    标签: python cython cythonize


    【解决方案1】:
    cpdef char* say_hello():
      message = json.dumps({"message": "Hello world"})
      return message.encode()
    

    这是一场等待发生的灾难(尽管 Cython 似乎无法生成警告)。 char* 指向的数据由 message.encode() 的结果保存 - 一个临时 Python 对象,在创建后立即被销毁。因此您的char* 立即无效。

    用 C 语言来思考这个问题:要返回 char*,某人必须拥有 char*,并且您应该确切地知道该所有者是谁。 没有一种简单、明智的方式来返回char*,而不负责分配/释放自己。除非您已准备好这样做并了解如何操作,否则我建议您从代码中删除所有 char* 并仅返回 Python 对象。


    您遇到的错误具体是因为 Cython 使用 cimport 来了解 Cython 函数。

    from utils cimport say_hello
    

    没有cimport 它认为say_hello 只是一个普通的Python 函数返回一个普通的Python 对象。但是,即使进行了此更改,您的代码仍然无效 - 您只是将错误移到 Cython 看不到的地方。

    【讨论】:

    • 非常感谢您的回答!我想问你一些事情,因为我对 C 不太熟悉。我的函数几乎总是会返回 char*(字节)。你能告诉我这样做的正确方法是什么吗?只是一些资源会很棒。再次感谢您的回答! :)
    • 问题在于 Cython 不能(也不能)保护您免受 C 语言的任何复杂性。因此,如果您不熟悉 C,最好的方法是返回 Python 对象,而不是 char*。如果你真的想使用char*,那么你应该用malloc分配它们,从Python字符串(可能是strcpy)复制数据并用free释放它们,确保你freemalloc 的一切(仅此而已)。
    • 感谢您的指导!我将对此进行详细调查。
    猜你喜欢
    • 1970-01-01
    • 2015-11-29
    • 2014-07-20
    • 1970-01-01
    • 2014-11-22
    • 2010-12-29
    • 2012-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多