Nim连接到Python,Python3连接MySQL,原文:https:/
Nim连接到Python,Python3连接MySQL,原文:https:/
原文:https://akehrer.github.io/nim/2015/01/24/connecting-nim-to-python.html
在以前的文章中在最后询问了关于Nim连接Python接口的代码,在经过一些实验后我能够做一些工作了。所以,让我们一起看一看。
Compilingalibrary
首先我们要谈到的是Nim编译器。在大多数情况下你把Nim代码编译成可执行文件要运行这个命令。
nim c projectfile.nim
但是要想把代码写到一个库中,我们将需要看编译器的文档了。这里有很多选择,但是--app是我们感兴趣的,在我们的例子中,我们将使用-app命令去创建一个共享库。(.dll in Windows, .so in linux, .dylib in OSX).
nim c --app:lib projectfile.nim
运行以上的命令能够在.nim文件的目录下创建一个projectfile.dll或libprojectfile.so文件。这简直太棒了,但是它并没有给到我们想要的。这个库已经被创建,但是没有我们已经暴露出来的功能函数。不是非常有用。
The exportc & dynlib pragmas
Nim有特殊的方法访问pragmas,当它解析特殊的代码块时会得到编译器额外的信息。我们早在以前的文章中看到它们的使用,记得下面的代码从math.h中加载isnan函数。
import mathproc cIsNaN(x: float): int {.importc: "isnan", header: "<math.h>".} ## returns non-zero if x is not a number
在上面的代码{.and.}之间包含importc语句,这是要告诉编译器在Nim中的cIsNaN过程要用到math.h中的isnan函数。
所以,如果Nim有导入C代码的方法,那么也应该有方法把代码导出到C中;它是exportc。用exportc能告诉编译器要显示我们的函数到外面。加上dynlib语句来确保能从访问库中访问我们的过程。
让我们从一些简单的例子开始:
proc summer*(x, y: float): float {. exportc, dynlib .} = result = x + y
保存它为test1.nim,在Windows下运行命令nimc--app:libtest1.nim会得到一个test1.dll文件。现在让我们看看是否能用它。(在linux下会生成libtest1.so文件)
Python ctypes
为了使用编译过的库,我们将会用到python中的ctypes模块,从2.5版本后它已经是标准库的一部分了。它允许我们使用被编译在库中基于C的代码,要在Python类型与C类型之间转换。这里的代码能访问我们的summer函数。
from ctypes import *def main(): test_lib = CDLL('test1') # Function parameter types test_lib.summer.argtypes = [c_float, c_float] # Function return types test_lib.summer.restype = c_float sum_res = test_lib.summer(1.0, 3.0) print('The sum of 1.0 and 3.0 is: %f'%sum_res)if __name__ == '__main__': main()
我们能够看到在加载了库后,设置了参数和函数的返回类型,然后传了两个参数调用这个函数。让我们看看发生了什么。
C:\Workspaces\nim-tests>python test1.pyThe sum of 1.0 and 3.0 is: 32.000008
嗯,这是不正确的。看起来我们可能没有使用正确的参数和返回类型。让我们比较一下Nim的float型和Pythonctypes的c_float型。根据Nim手册,float型设置为最快处理器的浮点类型。Pythonctypes手册说c_float是与C中的float型一样。是因为我用32位版本的Nim运行这段代码和2.7版本的Python在64位windows机器是Nim编译器使它的float为double?
from ctypes import *def main(): test_lib = CDLL('test1') # Function parameter types test_lib.summer.argtypes = [c_double, c_double] # Function return types test_lib.summer.restype = c_double sum_res = test_lib.summer(1.0, 3.0) print('The sum of 1.0 and 3.0 is: %f'%sum_res)if __name__ == '__main__': main()
C:\Workspaces\nim-tests>python test1.pyThe sum of 1.0 and 3.0 is: 4.000000
看起来已经解决了。当我们知道我们将使用exportc或者创建一个共享库,Nim有让我们添加更多约束的类型,减少这些类型的混乱(e.g.Cfloat,cint)。
openArray arguments & the header file
现在让我们来尝试更复杂的事情,使用之前两篇文章写的statistics模块中的median函数。
Nim code:
proc median*(x: openArray[float]): float {. exportc, dynlib .} = ## Computes the median of the elements in `x`. ## If `x` is empty, NaN is returned. if x.len == 0: return NAN var sx = @x # convert to a sequence since sort() won't take an openArray sx.sort(system.cmp[float]) if sx.len mod 2 == 0: var n1 = sx[(sx.len - 1) div 2] var n2 = sx[sx.len div 2] result = (n1 + n2) / 2.0 else: result = sx[(sx.len - 1) div 2]
Python code:
1 from ctypes import * 2 3 def main(): 4 test_lib = CDLL('test1') 5 6 # Function parameter types 7 test_lib.summer.argtypes = [c_double, c_double] 8 test_lib.median.argtypes = [POINTER(c_double), c_int] 9 10 # Function return types11 test_lib.summer.restype = c_double12 test_lib.median.restype = c_double13 14 # Calc some numbers15 nums = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]16 nums_arr = (c_double * len(nums))()17 for i,v in enumerate(nums):18 nums_arr[i] = c_double(v)19 20 sum_res = test_lib.summer(1.0, 3.0)21 print('The sum of 1.0 and 3.0 is: %f'%sum_res)22 23 med_res = test_lib.median(nums_arr, c_int(len(nums_arr)))24 print('The median of %s is: %f'%(nums, med_res))25 26 if __name__ == '__main__':27 main()
有一些有趣的事情发生,这里允许我们调用median过程。在这个Nim代码中你能够看到它仅仅需要一个参数,一个float型的openArray。像我们在第二篇文章中看到的,openArrays使传送不同大小的数组给过程成为可能。但是怎样把它转化进C库中呢?(你可能会从python代码入手)。一件事情可能会帮助我们,就是一个能够被传送到Nim编译器的额外的参数。
nim c --app:lib --header test1.nim
这个--header选项将会在模块被编译的文件夹nimcache中产生一个C头文件。如果我们看到头文件会像下面一样:
1 /* Generated by Nim Compiler v0.10.2 */ 2 /* (c) 2014 Andreas Rumpf */ 3 /* The generated code is subject to the original license. */ 4 /* Compiled for: Windows, i386, gcc */ 5 /* Command for C compiler: 6 gcc.exe -c -w -IC:\NIM\lib -o c:\workspaces\nim-tests\nimcache\test1.o c:\workspaces\nim-tests\nimcache\test1.h */ 7 #ifndef __test1__ 8 #define __test1__ 9 #define NIM_INTBITS 3210 #include "nimbase.h"11 N_NOCONV(void, signalHandler)(int sig);12 N_NIMCALL(NI, getRefcount)(void* p);13 N_LIB_IMPORT N_CDECL(NF, median)(NF* x, NI xLen0);14 N_LIB_IMPORT N_CDECL(NF, summer)(NF x, NF y);15 N_LIB_IMPORT N_CDECL(void, NimMain)(void);16 #endif /* __test1__ */
重要的是第13和14行,我们两个输出的过程被唤起。能够看到summer是要两个NF类型的参数,我们可以假设为Nim中的float型。另一方面median不是像我们在Nim过程中定义的一个参数,而是两个,一个是指向NF的指针,另一个是Nim中的整型NI。有一个关于整型的暗示是一个值的长度。所以这个openArray参数已经被转化为在C中标准的传递数组的方式,一个指针指向数组和一个数组的长度。
在Python代码中你能够看到我们设置了正确的参数([POINTER(c_double),c_int)和返回类型(c_double)和在15行到18行我们把一个python列表映射到C的double型数组中。然后当我们在23行调用这个函数,确保列表的长度转化为一个c_int。让我们检测一下结果。
C:\Workspaces\nim-tests>python test1.pyThe sum of 1.0 and 3.0 is: 4.000000The median of [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] is: 4.500000
这看起来是好的,它对于这篇文章来说是足够的。在以后的文章中我们将看到更多类型的接口,像字符串,元组和自定义对象。
Reference
NimCompilerUserGuide
NimManual
Pythonctypes
PythonctypesTutorial
(Thankstodom96forcorrections.)
Nim连接到Python
相关内容
- 暂无相关文章
评论关闭