如何使用 Cython 将外部 C 函数导入 IPython Notebook?
How can I import an external C function into an IPython Notebook using Cython?
我想使用 Cython 将 C 函数导入到 IPython 笔记本中。目前,我正在尝试复制 Cython documentation 中的示例,但出现编译错误。
我的 Python 代码(来自 iPython 笔记本):
import cython
%load_ext Cython
------------------------------------新单元格
%%cython
cdef extern from "spam.c":
void order_spam(int tons)
我的C代码:
// spam.c
#include <stdio.h>
static void order_spam(int tons)
{
printf("Ordered %i tons of spam!\n", tons);
}
运行 这段代码,我得到以下回溯和错误信息:
CompileError Traceback (most recent call last)
<ipython-input-13-8bb733557977> in <module>()
----> 1 get_ipython().run_cell_magic(u'cython', u'', u'\ncdef extern from "spam.c":\n void order_spam(int tons)')
/Users/danielacker/anaconda2/lib/python2.7/site-packages/IPython/core/interactiveshell.pyc in run_cell_magic(self, magic_name, line, cell)
2118 magic_arg_s = self.var_expand(line, stack_depth)
2119 with self.builtin_trap:
-> 2120 result = fn(magic_arg_s, cell)
2121 return result
2122
<decorator-gen-126> in cython(self, line, cell)
/Users/danielacker/anaconda2/lib/python2.7/site-packages/IPython/core/magic.pyc in <lambda>(f, *a, **k)
191 # but it's overkill for just that one bit of state.
192 def magic_deco(arg):
--> 193 call = lambda f, *a, **k: f(*a, **k)
194
195 if callable(arg):
/Users/danielacker/anaconda2/lib/python2.7/site-packages/Cython/Build/IpythonMagic.py in cython(self, line, cell)
276 build_extension.build_temp = os.path.dirname(pyx_file)
277 build_extension.build_lib = lib_dir
--> 278 build_extension.run()
279 self._code_cache[key] = module_name
280
/Users/danielacker/anaconda2/lib/python2.7/distutils/command/build_ext.pyc in run(self)
337
338 # Now actually compile and link everything.
--> 339 self.build_extensions()
340
341 def check_extensions_list(self, extensions):
/Users/danielacker/anaconda2/lib/python2.7/distutils/command/build_ext.pyc in build_extensions(self)
446
447 for ext in self.extensions:
--> 448 self.build_extension(ext)
449
450 def build_extension(self, ext):
/Users/danielacker/anaconda2/lib/python2.7/distutils/command/build_ext.pyc in build_extension(self, ext)
496 debug=self.debug,
497 extra_postargs=extra_args,
--> 498 depends=ext.depends)
499
500 # XXX -- this is a Vile HACK!
/Users/danielacker/anaconda2/lib/python2.7/distutils/ccompiler.pyc in compile(self, sources, output_dir, macros, include_dirs, debug, extra_preargs, extra_postargs, depends)
572 except KeyError:
573 continue
--> 574 self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
575
576 # Return *all* object filenames, not just the ones we just built.
/Users/danielacker/anaconda2/lib/python2.7/distutils/unixccompiler.pyc in _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts)
120 extra_postargs)
121 except DistutilsExecError, msg:
--> 122 raise CompileError, msg
123
124 def create_static_lib(self, objects, output_libname,
CompileError: command 'gcc' failed with exit status 1
我已尝试搜索 Google 以查找此错误,但我似乎找不到任何相关信息。
%cython
魔法命令 probaby 似乎没有用。
%cython
魔术命令并不能真正完成这里的任务。为了编译它,您还需要提供 *.c
源文件,而 %cython
不允许(据我所知)。 (The source file for it indicates that it only uses the text entered in the cell as a source file.)
包装您的 C
函数:
在提出可能的解决方案之前,请允许我指出,您创建的 .pyx
文件实际上并未自动包装 C
函数 order_spam
。如果您在 cdef extern
块内将其指定为 cpdef
,则可以自动包装它(或者您可以在 cdef extern
块外自行包装它,这样可以提供更大的灵活性)。
我将为 Cython 文件使用 cyspam.pyx
的文件名:
cdef extern from "spam.c":
cpdef void order_spam(int tons)
请注意我是如何在函数声明前加上 cpdef
前缀的,这会指示 Cython 自动包装函数。
使用 setup.py
脚本:
为了完全控制编译过程,您通常需要创建一个 setup.py
脚本,其中包含指定的所有必需源,包括目录等。
setup.py
脚本应如下所示:
from distutils.core import setup, Extension
from Cython.Build import cythonize
# you specify the c source file in the sources list
ext = Extension('cyspam', sources = ['cyspam.pyx', 'spam.c'])
setup(name="C spam", ext_modules = cythonize([ext]))
您可以通过简单的文本编辑器或通过 IPython
使用 %%writefile
魔法命令创建这样的文件。 setup.py
脚本当然应该放在与 cyspam.pyx
和 spam.c
文件相同的目录中。
编译文件:
您可以为此打开终端或使用 %%bash
命令从 IPython
,无论哪种方式都可以。发出以下命令:
python setup.py build_ext --inplace
--inplace
将生成的 .so
文件放在当前目录中。
完成这些后,您可以轻松地将文件 cyspam
导入 Ipython
并调用包装的 C
函数:
汇总来自 IPython
的所有命令:
总而言之,如果您只想从 IPython
执行此操作,您将发出以下命令:
In [1]: %%writefile setup.py
....: from distutils.core import setup, Extension
....: from Cython.Build import cythonize
....: ext = Extension('cyspam', sources = ['cyspam.pyx', 'spam.c'])
....: setup(name="C spam", ext_modules = cythonize([ext]))
In [2]: %%bash
...: python setup.py build_ext --inplace
In [3]: import cyspam
In [4]: cyspam.order_spam(1000)
You ordered 1000 ammount of spam!
作为替代方案,您始终可以创建一个 .pyxbld
文件来指定 pyximport.install()
所需的参数。这提供了相同级别的控制,但对于已经有使用 setup.py
脚本经验的 Python 用户来说很可能违反直觉。
参见相关:
How can I set Cython compiler flags when using pyximport?
我想使用 Cython 将 C 函数导入到 IPython 笔记本中。目前,我正在尝试复制 Cython documentation 中的示例,但出现编译错误。
我的 Python 代码(来自 iPython 笔记本):
import cython
%load_ext Cython
------------------------------------新单元格
%%cython
cdef extern from "spam.c":
void order_spam(int tons)
我的C代码:
// spam.c
#include <stdio.h>
static void order_spam(int tons)
{
printf("Ordered %i tons of spam!\n", tons);
}
运行 这段代码,我得到以下回溯和错误信息:
CompileError Traceback (most recent call last)
<ipython-input-13-8bb733557977> in <module>()
----> 1 get_ipython().run_cell_magic(u'cython', u'', u'\ncdef extern from "spam.c":\n void order_spam(int tons)')
/Users/danielacker/anaconda2/lib/python2.7/site-packages/IPython/core/interactiveshell.pyc in run_cell_magic(self, magic_name, line, cell)
2118 magic_arg_s = self.var_expand(line, stack_depth)
2119 with self.builtin_trap:
-> 2120 result = fn(magic_arg_s, cell)
2121 return result
2122
<decorator-gen-126> in cython(self, line, cell)
/Users/danielacker/anaconda2/lib/python2.7/site-packages/IPython/core/magic.pyc in <lambda>(f, *a, **k)
191 # but it's overkill for just that one bit of state.
192 def magic_deco(arg):
--> 193 call = lambda f, *a, **k: f(*a, **k)
194
195 if callable(arg):
/Users/danielacker/anaconda2/lib/python2.7/site-packages/Cython/Build/IpythonMagic.py in cython(self, line, cell)
276 build_extension.build_temp = os.path.dirname(pyx_file)
277 build_extension.build_lib = lib_dir
--> 278 build_extension.run()
279 self._code_cache[key] = module_name
280
/Users/danielacker/anaconda2/lib/python2.7/distutils/command/build_ext.pyc in run(self)
337
338 # Now actually compile and link everything.
--> 339 self.build_extensions()
340
341 def check_extensions_list(self, extensions):
/Users/danielacker/anaconda2/lib/python2.7/distutils/command/build_ext.pyc in build_extensions(self)
446
447 for ext in self.extensions:
--> 448 self.build_extension(ext)
449
450 def build_extension(self, ext):
/Users/danielacker/anaconda2/lib/python2.7/distutils/command/build_ext.pyc in build_extension(self, ext)
496 debug=self.debug,
497 extra_postargs=extra_args,
--> 498 depends=ext.depends)
499
500 # XXX -- this is a Vile HACK!
/Users/danielacker/anaconda2/lib/python2.7/distutils/ccompiler.pyc in compile(self, sources, output_dir, macros, include_dirs, debug, extra_preargs, extra_postargs, depends)
572 except KeyError:
573 continue
--> 574 self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
575
576 # Return *all* object filenames, not just the ones we just built.
/Users/danielacker/anaconda2/lib/python2.7/distutils/unixccompiler.pyc in _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts)
120 extra_postargs)
121 except DistutilsExecError, msg:
--> 122 raise CompileError, msg
123
124 def create_static_lib(self, objects, output_libname,
CompileError: command 'gcc' failed with exit status 1
我已尝试搜索 Google 以查找此错误,但我似乎找不到任何相关信息。
%cython
魔法命令 probaby 似乎没有用。
%cython
魔术命令并不能真正完成这里的任务。为了编译它,您还需要提供 *.c
源文件,而 %cython
不允许(据我所知)。 (The source file for it indicates that it only uses the text entered in the cell as a source file.)
包装您的 C
函数:
在提出可能的解决方案之前,请允许我指出,您创建的 .pyx
文件实际上并未自动包装 C
函数 order_spam
。如果您在 cdef extern
块内将其指定为 cpdef
,则可以自动包装它(或者您可以在 cdef extern
块外自行包装它,这样可以提供更大的灵活性)。
我将为 Cython 文件使用 cyspam.pyx
的文件名:
cdef extern from "spam.c":
cpdef void order_spam(int tons)
请注意我是如何在函数声明前加上 cpdef
前缀的,这会指示 Cython 自动包装函数。
使用 setup.py
脚本:
为了完全控制编译过程,您通常需要创建一个 setup.py
脚本,其中包含指定的所有必需源,包括目录等。
setup.py
脚本应如下所示:
from distutils.core import setup, Extension
from Cython.Build import cythonize
# you specify the c source file in the sources list
ext = Extension('cyspam', sources = ['cyspam.pyx', 'spam.c'])
setup(name="C spam", ext_modules = cythonize([ext]))
您可以通过简单的文本编辑器或通过 IPython
使用 %%writefile
魔法命令创建这样的文件。 setup.py
脚本当然应该放在与 cyspam.pyx
和 spam.c
文件相同的目录中。
编译文件:
您可以为此打开终端或使用 %%bash
命令从 IPython
,无论哪种方式都可以。发出以下命令:
python setup.py build_ext --inplace
--inplace
将生成的 .so
文件放在当前目录中。
完成这些后,您可以轻松地将文件 cyspam
导入 Ipython
并调用包装的 C
函数:
汇总来自 IPython
的所有命令:
总而言之,如果您只想从 IPython
执行此操作,您将发出以下命令:
In [1]: %%writefile setup.py
....: from distutils.core import setup, Extension
....: from Cython.Build import cythonize
....: ext = Extension('cyspam', sources = ['cyspam.pyx', 'spam.c'])
....: setup(name="C spam", ext_modules = cythonize([ext]))
In [2]: %%bash
...: python setup.py build_ext --inplace
In [3]: import cyspam
In [4]: cyspam.order_spam(1000)
You ordered 1000 ammount of spam!
作为替代方案,您始终可以创建一个 .pyxbld
文件来指定 pyximport.install()
所需的参数。这提供了相同级别的控制,但对于已经有使用 setup.py
脚本经验的 Python 用户来说很可能违反直觉。
参见相关:
How can I set Cython compiler flags when using pyximport?