浅谈 Python 程序和 C 程序的整合

  概览

  Python 是种用于快速开发软件Software编程语言语法比较简单易于掌握但存在执行速度慢问题并且在处理某些问题时存在不足如对计算机硬件系统访问对媒体文件访问等而作为软件Software开发传统编程语言—— C 语言却能在这些问题上很好地弥补 Python 语言不足因此本文通过例子研究如何在 Python 中整合既有 C 语言模块包括用 C 语言编写和动态链接库等从而充分发挥 Python 语言和 C 语言各自优势

  背景知识介绍

  Python 语言特点

  Python 作为开发语言被越来越多地运用到快速开发Python 是种解释型互动面向对象编程语言它包含了模块化操作异常处理动态资料形态以及类型使用语法表达优美易读具有很多优秀脚本语言特点:解释面向对象内建高级数据结构支持模块和包支持多种平台可扩展而且它还支持交互式方式运行图形方式运行它拥有众多编程界面支持各种操作系统平台以及众多各类利用 C 和 C 可以对它进行扩充

  C 语言特点

  C 语言作为最受人们欢迎语言的有广泛发展基础简洁紧凑、灵活方便功能强大是其特点另外C 语言是门中级语言它把高级语言基本结构和语句和低级语言实用性结合起来由于可以直接访问物理地址可以方便对硬件进行操作因此很多系统软件Software都是由 C 语言编写

  Python 语言和 C 语言交互

  为了节省软件Software开发成本软件Software开发人员希望能够缩短软件Software开发时间希望能够在短时间内开发出稳定产品Python 功能强大简单易用能够快速开发应用软件Software但是由于 Python 自身执行速度局限性对性能要求比较高模块需要使用效率更高语言进行开发例如 C 语言系统其他模块运用 Python 进行快速开发最后将 C 语言开发模块和 Python 开发模块进行整合在此背景下基于 Python 语言和 C 语言各自特点用 C 语言来扩展现有 Python 显得很有意义本文首先介绍几种常用整合 Python 和 C 语言思路方法最后给出相应例子

  利用 ctypes 模块整合 Python 和 C

  ctypes 模块

  ctypes 是 Python 个标准模块它包含在 Python2.3 及以上版本里ctypes 是个 Python 高级外部接口它使得 Python 可以 C 语言编译静态链接库和动态链接库运用 ctypes 模块能够在 Python 源中创建访问和操作简单或复杂 C 语言数据类型最为重要是 ctypes 模块能够在多个平台上工作包括 WindowsWindows CEMac OS XLinuxSolarisFreeBSDOpenBSD

  接下来通过几个简单例子来看下 ctypes 模块如何整合 Python 和 C

  源代码层面上整合

  利用 Python 本身提供 ctypes 模块可以使 Python 语言和 C 语言在源代码层面上进行整合本节介绍了如何通过使用 ctypes 库在 Python 中可以定义类似 C 语言变量

  下表列出了 ctypes 变量类型C 语言变量类型和 Python 语言变量类型的间关系:

表 1. ctypesc 语言和 Python 语言变量类型关系

ctypes type c type Python type
c_char char 1-character
c_wchar wchar_t 1-character unicode
c_ char /long
c_u unsigned char /long
c_ /long
c_u unsigned /long
c_ /long
c_u unsigned /long
c_long long /long
c_ulong unsigned long /long
c_longlong __64 or long long /long
c_ulonglong unsigned __64 or unsigned long long /long
c_float float float
c_double double float
c_char_p char * (NUL terminated) or None
c_wchar_p wchar_t * (NUL terminated) unicode or None
c_void_p void * /long or None



  表 1 中列是在 ctypes 库中定义变量类型第 2列是 C 语言定义变量类型第 3列是 Python 语言在不使用 ctypes 时定义变量类型

  举例:

清单 1. ctypes 简单使用

 >>> from ctypes import *        # 导入 ctypes 库中所有模块 
 >>> i = c_(45)            # 定义个  型变量值为 45 
 >>> i.value                # 打印变量值 
 45 
 >>> i.value = 56             # 改变该变量值为 56 
 >>> i.value                # 打印变量新值 
 56 


  从下面例子可以更明显地看出 ctypes 里变量类型和 C 语言变量类型相似性:

清单 2. ctypes 使用 C 语言变量

 >>> p = create__buffer(10)   # 定义个可变串变量长度为 10 
 >>> p.raw                 # 值是全 0即 C 语言中串结束符’ \0 ’ 
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 
 >>> p.value = "Student"         # 串赋值 
 >>> p.raw                 # 后 3个仍是’ \0 ’ 
'Student\x00\x00\x00' 
 >>> p.value = "Big"           # 再次赋值 
 >>> p.raw                  # 只有前 3个被修改第 4个被修改为’ \0 ’ 
'Big\x00ent\x00\x00\x00' 


  下面例子介绍说明了指针操作:

清单 3. ctypes 使用 C 语言指针

 >>> i = c_(999)                 # 定义  类型变量 i值为 999 
 >>> pi = poer(i)                # 定义指针指向变量 i 
 >>> pi.contents                   # 打印指针所指内容 
 c_long(999) 
 >>> pi.contents = c_long(1000)          # 通过指针改变变量 i 值 
 >>> pi.contents                   # 打印指针所指内容 
 c_long(1000) 


  下面例子介绍说明了结构和操作:

清单 4. ctypes 使用 C 语言和结构体

 >>>  POINT(Structure):         # 定义个结构内含两个成员变量 xy均为  型 
 ...   _fields_ = [("x", c_), 
 ...         ("y", c_)] 
 ... 
 >>> po = POINT(2,5)           # 定义个 POINT 类型变量值为 x=2, y=5 
 >>> pr po.x, po.y           # 打印变量 
 2 5 
 >>> po = POINT(y=5)              # 重新定义个 POINT 类型变量x 取默认值 
 >>> pr po.x, po.y           # 打印变量 
 0 5 
 >>> POINT_ARRAY = POINT * 3          # 定义 POINT_ARRAY 为 POINT 类型 
 # 定义个 POINT 内含 3个 POINT 变量 
 >>> pa = POINT_ARRAY(POINT(7, 7), POINT(8, 8), POINT(9, 9))  
 >>> for p in pa: pr p.x, p.y        # 打印 POINT 中每个成员值 
 ... 
 7 7 
 8 8 
 9 9 


  Python 访问 C 语言 dll

  通过 ctypes 模块Python 可以访问 C 语言编译 dll本节通过个简单例子Python helloworld.py 中 some.dll 中 helloworld 来介绍 Python 如何 windows 平台上 dll

  导入动态链接库

清单 5. ctypes 导入 dll

 from ctypes import windll # 首先导入 ctypes 模块 windll 子模块 
 somelibc = windll.LoadLibrary(some.dll) # 使用 windll 模块 LoadLibrary 导入动态链接库 


  访问动态链接库中

清单 6. ctypes 使用 dll 中

 somelibc. helloworld # 这样就可以得到 some.dll  helloworld 返回值 

  整个 helloworld.py 是这样:


清单 7. Python hellpworld 代码

 from ctypes import windll 
 
 def callc: 
 # load the some.dll 
 somelibc = windll.LoadLibrary(some.dll) 
 pr somelibc. helloworld 
  __name__ “____”: 
 callc 


  在命令行运行 helloworld.py在 console 上可以看到 some.dll 中 helloworld 输出


清单 8. Python hellpworld Windows command console 运行输出

 C:\>python C:\python\test\helloworld.py 
 Hello World! Just a simple test. 


  Python C 语言 so

  通过 ctypes 模块Python 也可以访问 C 语言编译 so 文件和 Python C dll 思路方法基本相同本节通过个简单例子Python helloworld.py 中 some.so 中 helloworld 来介绍 Python 如何 linux 平台上 so

  导入动态链接库

清单 9. ctypes 导入 so

 from ctypes import cdll    
 # 首先导入 ctypes 模块 cdll 子模块注意 linux 平台上使用 cdll 而不是 windll 
 somelibc = cdll.LoadLibrary(“./some.so”) 
 # 使用 cdll 模块 LoadLibrary 导入动态链接库 


  访问动态链接库中

清单 10. ctypes 使用 so 中

 somelibc. helloworld # 使用思路方法和 windows 平台上是 

  整个 helloworld.py 是这样:


清单 11. Python helloworld 代码

 from ctypes import cdll 
 
 def callc: 
 # load the some.so 
 somelibc = cdll.LoadLibrary(some.so) 
 pr somelibc. helloworld 
  __name__ “____”: 
 callc 


  在命令行运行 helloworld.py在 linux 标准输出上可以看到 some.so 中 helloworld 输出


清单 12. Python hellpworld Linux shell 运行输出

 [root@linux-790t] python ./helloworld.py 
 Hello World! Just a simple test. 


  Python 和 C 整合例子

  以下我们举例用 Python 来实现个小工具用来实现 hash 算法查看文件校验和(MD5,CRC,SHA1 等等)通过查看文件校验和可以知道文件在传输过程中是否被破坏或篡改

  Hash般翻译做“散列”也有直接音译为"哈希"就是把任意长度输入(又叫做预映射pre-image)通过散列算法变换成固定长度输出该输出就是散列值这种转换是种压缩映射也就是散列值空间通常远小于输入空间区别输入可能会散列成相同输出而不可能从散列值来唯确定输入值简单说就是种将任意长度消息压缩到某固定长度消息摘要

  由于相对 C 语言来说Python 运行效率较低因此我们 Python 小工具利用个已有 C 语言动态链接库 (hashtcalc.dll) 来实现我们本例中我们运用 wxPython 编写简单 GUI 界面通过 python hashtcalc.dll 接口计算文件校验和然后输出在界面上

  架构图

图 1. 工具架构图


  hashcalc.dll 接口描述

  名:calc_CRC32

  :char* calc_CRC32(char *filename);

  参数:文件名

  返回值:

  介绍说明:该对输入文件内容进行计算并且返回它 CRC32

  名:calc_MD5

  :char* calc_MD5(char *filename);

  参数:文件名

  返回值:

  介绍说明:该对输入文件内容进行计算并且返回它 MD5

  名:calc_SHA1

  :char* calc_SHA1 (char *filename);

  参数:文件名

  返回值:

  介绍说明:该对输入文件内容进行计算并且返回它 SHA1

  HashcalcAdapter 代码

  HashcalcAdapter.py 实现了个 python HashcalcAdapterHashcalcAdapter 对 hashtcalc.dl C 语言接口进行了封装使得其他 python 模块可以直接通过 HashcalcAdapter 使用 hashtcalc.dll 中实现 hash 算法具体代码如下:

清单 13. HashcalcAdapter.py 代码

 from ctypes import windll 
 from ctypes import * 
 
  HashcalcAdapter(object): 
  def __init__(self, dllpath): 
    self._dllpath = dllpath 
    self._libc = windll.LoadLibrary(self._dllpath) 
 
  def calc_CRC32(self, filename): 
  _filename = c_char_p(filename) 
   self._libc.calc_CRC32(_filename) 
 
  def calc_MD5(self, filename): 
  _filename = c_char_p(filename) 
   self._libc.calc_MD5(_filename) 
 
  def calc_SHA1(self, filename): 
  _filename = c_char_p(filename) 
   self._libc.calc_SHA1(_filename) 


  运行界面

图 2. 工具运行界面


  查看原图(大图)

  整理总结

  在软件Software开发过程中同时运用 Python 语言和 C 语言既能够在加快开发速度同时也能够保证软件Software运行性能

Tags: 

延伸阅读

最新评论

发表评论