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

相关内容

    暂无相关文章

评论关闭