分享

[学习Cython编程]C中使用Python标准库(urllib,logging)

 心不留意外尘 2016-08-05

http://blog.csdn.net/yueguanghaidao/article/details/23093981

2014

Python的库非常丰富,如果能在C中使用Python的一些库,无疑是很让人兴奋的,下面我们就将在C中使用Pyhton的urllib模块和logging模块。

在C中调用Python需要包含整个Python的运行时库,链接模型如下:


在C/C++中嵌入Python代码是非常简单的,下面是代码模板:

[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. #include <Python.h>  
  2. int main(int argc,char** argv)  
  3. {  
  4.     Py_SetProgramName(argv[0]);  
  5.     Py_Initialized();  
  6.     /* Do all your stuff in side here... */  
  7.     Py_Finalize();  
  8.     return 0;  
  9. }  

注意“Pyhon.h"头文件需要首先被包含。

我们通过下面几个例子学习如何嵌入Python代码:


一:urllib

Python关于网络方面的库相当丰富,我们将在C中调用urllib.urlopen获取网页内容。

url.pyx文件:

[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. import urllib  
  2.   
  3. cdef public char * open(char * url):  
  4.     content=urllib.urlopen(url).read()  
  5.     return content  
public是Cython的关键字,代表这个函数被导出,所以Cython会自动创建url.c和url.h,url.h就是用来被C/C++代码包含的,Cython做的非常智能。

Python中的字符串对应的就是C中的char*,所以我们参数为char*,同时返回内容也为char*,Cython将自动帮我们完成类型转换。

我们来看看Cython自动产生的url.h头文件,我们将在主文件中包含它。

url.h文件:

[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. #ifndef __PYX_HAVE__url  
  2. #define __PYX_HAVE__url  
  3.   
  4.   
  5. #ifndef __PYX_HAVE_API__url  
  6.   
  7. #ifndef __PYX_EXTERN_C  
  8.   #ifdef __cplusplus  
  9.     #define __PYX_EXTERN_C extern "C"  
  10.   #else  
  11.     #define __PYX_EXTERN_C extern  
  12.   #endif  
  13. #endif  
  14.   
  15. __PYX_EXTERN_C DL_IMPORT(char) *open(char *);  
  16.   
  17. #endif /* !__PYX_HAVE_API__url */  
  18.   
  19. #if PY_MAJOR_VERSION < 3  
  20. PyMODINIT_FUNC initurl(void);  
  21. #else  
  22. PyMODINIT_FUNC PyInit_url(void);  
  23. #endif  
  24.   
  25. #endif /* !__PYX_HAVE__url */  
这里面主要有两个函数,一个是我们用public关键字导出的open函数,另外一个在Python2平台上是initurl函数,这将初始化我们的模块。注意:”initurl"中的“url"就是我们的文件名,在Python中也就是模块。

下面我们将在C中调用open函数,代码为:

main.c文件:

[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. #include <Python.h>  
  2. #include "url.h"  
  3.   
  4. int main (int argc, char ** argv)  
  5. {  
  6.   /* Boiler plate init Python */  
  7.   Py_SetProgramName (argv [0]);  
  8.   Py_Initialize ();  
  9.   
  10.   /* Init our url module into Python memory */  
  11.   initurl();  
  12.   
  13.   if (argc >= 2)  
  14.     {  
  15.       /* call directly into our cython module */  
  16.        printf("%s",open(argv[1]));  
  17.     }  
  18.   else  
  19.     printf ("require url...\n");  
  20.   
  21.   /* cleanup python before exit ... */  
  22.   Py_Finalize ();  
  23.   
  24.   return 0;  
  25. }  
我们只是简单的在C中打印出open的获取结果,注意先调用初始模块函数,然后在调用模块中可用方法。

Makefile文件如下:

[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. all:  
  2.     cython url.pyx  
  3.     gcc -g -O2 -fpic -c url.c -o url.o `python-config --includes`  
  4.     gcc -g -O2 -fpic -c main.c -o main.o `python-config --includes`  
  5.     gcc -g -O2 -o example main.o url.o `python-config --libs`  
  6.   
  7. clean:  
  8.     rm -f example url.c *.o  
我们看看运行结果:



很强大吧,有了Cython咱再也不怕C的标准库功能单一了。大笑

认真看代码的童鞋对url.pyx可能会有疑惑,为什么不直接return呢,加个零时变量做啥,这是有原因的:

[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. import urllib  
  2.   
  3. cdef public char * open(char * url):  
  4.     return urllib.urlopen(url).read()  

如果url.pyx代码如上,编译产生错误:obtaining char* from temporary Python value


由于我们之间return将导致read()产生零时变量,当超出该函数作用域零时变量将被释放,导致我们返回的char*指针成为悬挂指针。

而当我们专门赋值为一变量时,将导致引用计数加一,变量生存周期也就在我们控制之下了。


二:logging

下面演示如何在C中使用Python的logging模块。

logger.pyx文件如下:
[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. import logging  
  2.   
  3. cdef public void initLogging (char * logfile):  
  4.     logging.basicConfig (filename = logfile,  
  5.                          level = logging.DEBUG,  
  6.                          format = '%(levelname)s %(asctime)s: %(message)s',  
  7.                          datefmt = '%m/%d/%Y %I:%M:%S')  
  8.   
  9. cdef public void pyinfo (char * message):  
  10.     logging.info (message)  
  11.   
  12. cdef public void pydebug (char * message):  
  13.     logging.debug (message)  
  14.   
  15. cdef public void pyerror (char * message):  
  16.     logging.error (message)  
main.h头文件如下:
[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. #ifndef __MAIN_H__  
  2. #define __MAIN_H__  
  3.   
  4. #include <Python.h>  
  5.   
  6. #include <stdio.h>  
  7. #include <stdarg.h>  
  8.   
  9. #define printflike \  
  10.   __attribute__ ((format (printf, 3, 4)))  
  11.   
  12. extern void printflike cinfo (const char *, unsigned, const char *, ...);  
  13. extern void printflike cdebug (const char *, unsigned, const char *, ...);  
  14. extern void printflike cerror (const char *, unsigned, const char *, ...);  
  15.   
  16. #define info(...)                               \  
  17.   cinfo (__FILE__, __LINE__, __VA_ARGS__)  
  18.   
  19. #define error(...)                              \  
  20.   cerror (__FILE__, __LINE__, __VA_ARGS__)  
  21.   
  22. #define debug(...)                              \  
  23.   cdebug (__FILE__, __LINE__, __VA_ARGS__)  
  24.   
  25. #include "logger.h"  
  26.   
  27. #endif //__MAIN_H__  
main.h头文件主要对man.c中函数的封装,自动传递文件名和行号。里面使用了变长参数,并且使用了GCC的__attrribute__特性。
#define printflike __attribute__ ((format (printf, 3, 4)))说明后面格式化字符串使用printf函数格式,并且变长参数从第三个参数开始,这更多的是让GCC可在编译阶段发现错误。

mian.c文件如下:
[python] view plain copy
在CODE上查看代码片派生到我的代码片
  1. #include "main.h"  
  2.   
  3. void cinfo (const char * file, unsigned line,  
  4.             const char * fmt, ...)  
  5. {  
  6.   char buffer [256];  
  7.   va_list args;  
  8.   va_start (args, fmt);  
  9.   vsprintf (buffer, fmt, args);  
  10.   va_end (args);  
  11.   
  12.   char buf [512];  
  13.   snprintf (buf, sizeof (buf), "%s-%i -> %s",  
  14.             file, line, buffer);  
  15.   pyinfo (buf);  
  16. }  
  17.   
  18. void cdebug (const char * file, unsigned line,  
  19.              const char * fmt, ...)  
  20. {  
  21.   char buffer [256];  
  22.   va_list args;  
  23.   va_start (args, fmt);  
  24.   vsprintf (buffer, fmt, args);  
  25.   va_end (args);  
  26.   
  27.   char buf [512];  
  28.   snprintf (buf, sizeof (buf), "%s-%i -> %s",  
  29.             file, line, buffer);  
  30.   pydebug (buf);  
  31. }  
  32.   
  33. void cerror (const char * file, unsigned line,  
  34.              const char * fmt, ...)  
  35. {  
  36.   char buffer [256];  
  37.   va_list args;  
  38.   va_start (args, fmt);  
  39.   vsprintf (buffer, fmt, args);  
  40.   va_end (args);  
  41.   
  42.   char buf [512];  
  43.   snprintf (buf, sizeof (buf), "%s-%i -> %s",  
  44.             file, line, buffer);  
  45.   pyerror (buf);  
  46. }  
  47.   
  48. int main (int argc, char ** argv)  
  49. {  
  50.   /* Boiler plate init Python */  
  51.   Py_SetProgramName (argv [0]);  
  52.   Py_Initialize ();  
  53.   
  54.   /* Init our config module into Python memory */  
  55.   initlogger ();  
  56.   
  57.   if (argc >= 2)  
  58.     {  
  59.       /* call directly into our cython module parseConfig */  
  60.       initLogging (argv [1]);  
  61.   
  62.       info ("info message");  
  63.       debug ("debug message");  
  64.       error ("error message");  
  65.     }  
  66.   else  
  67.     printf ("require path to output logfile...\n");  
  68.   
  69.   /* cleanup python before exit ... */  
  70.   Py_Finalize ();  
  71.   
  72.   return 0;  
  73. }  

我们来看看运行结果:




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多