2.22 混合编程
2.22.1 什么是混合编程
所谓的混合编程就是指在一个大型项目中,因为要涉及到多个方面,单独使用某一种语言进行开发已经不能满足要求,可能在某一个模块中,需要用A语言编写一部分,而用B语言编写另外的部分。像这种使用两种或两种以上的程序设计语言来开发应用程序的过程就称为混合编程。
至今为止,计算机上流行的程序设计语言有多种,它们有各自的优势和不足,混合编程可以充分利用各种程序设计语言的优势。混合编程的关键问题是参数传递。
2.22.2 Python和C语言混合编程
Python和C语言混合编程是指在一个项目中同时使用Python和C语言来编写代码,并且两种语言之间进行交互和通信。
混合编程的目的是充分发挥各种语言的优势,使得程序能够更高效地运行和更灵活地开发。Python通常被用于开发高级逻辑、数据处理或用户界面等功能,而C语言则常用于底层系统编程、性能优化和硬件驱动等方面。
通过混合编程,Python可以调用C语言编写的底层库,从而提高程序的性能。同时,Python也可以作为高级语言调用C语言的接口,实现对底层的控制和扩展。
此外,混合编程还可以利用Python丰富的包管理工具和生态系统,使得C语言项目能够更好地集成进Python开发环境中,易于维护和扩展。
总之,混合编程结合了Python的高级特性和C语言的低级能力,使得开发者能够在不同需求和场景下选择合适的工具,达到更好的开发效果。
2.22.2.1 什么是windows API
Windows API(Application Programming Interface,应用程序编程接口)是微软公司为 Windows 操作系统提供的一组函数和过程的集合。它允许开发人员使用编程语言与操作系统交互,以创建各种类型的应用程序,如桌面应用、设备驱动程序、服务等。
Windows API 提供了丰富的功能,包括窗口管理、图形用户界面、文件系统、网络通信、多媒体处理、安全性等。开发人员可以使用这些函数和过程来操作窗口、处理用户输入、读写文件、进行网络传输、播放音频视频等。通过调用 Windows API,开发人员可以利用操作系统提供的底层功能,实现更加灵活和高效的应用程序开发。
常见的编程语言如C++、C#、VB.NET等都支持使用 Windows API 进行开发。开发人员可以参考微软的文档和示例代码来学习和使用 Windows API。
2.22.2.2 库文件和头文件
库文件(Library Files)是包含预编译的可重用二进制代码的文件。这些库可以包含函数、类、变量和其他可供程序使用的代码。当开发人员需要使用特定功能时,他们可以链接相应的库文件到他们的应用程序中,以便能够调用库中定义的函数和使用库中提供的功能。
头文件(Header Files)是包含外部库或模块中函数、数据结构和常量的声明的文件。这些声明使得开发人员可以了解如何正确地使用特定库或模块,并通过函数名、参数和返回类型来引导编译器,在编译时将相应的函数、结构或常量与其实际定义匹配起来。
开发人员使用头文件可以将外部库的功能集成到他们的代码中,并确保他们正确地使用这些功能。他们可以使用头文件来声明某个函数或变量,并在编译时将这些声明链接到相关的库文件,从而在运行时获得所需的功能。
库文件和头文件的区别
头文件(Header files)通常具有.h作为扩展名,并包含了函数和数据结构的声明(declaration)。它们描述了API函数和数据结构的名称、参数和返回值等信息,但没有实际的代码实现。头文件的主要作用是告诉编译器这些函数和数据结构的存在和定义,使得程序能够正确地调用和使用它们。程序在编译阶段会通过预处理命令(例如#include)将头文件中的声明插入到源代码中。
库文件(Library files)通常具有.lib或.dll作为扩展名。它们包含了API函数和数据结构的实际代码实现,以供程序在运行时调用。库文件可以分为静态库(Static libraries)和动态库(Dynamic libraries)。静态库在编译时被链接到程序中,使得程序¥¥运行,不需要依赖外部库文件。动态库则在运行时被动态加载并链接,多个程序可以共享同一个动态库文件,减小了可执行文件的大小。
总结起来,头文件用于声明API函数和数据结构的接口,使得程序能够在编译阶段正确地调用和使用它们;而库文件则包含了API函数和数据结构的实际代码实现,供程序在运行时调用。通过引用头文件和链接库文件,开发者可以方便地使用Windows API提供的各种功能。
常见的头文件
头文件 | 说明 |
---|---|
windows.h | 所有windows头文件的集合 |
windef.h | windows数据类型 |
winbase.h | kernel32的API |
wingdi.h | gdi32的API |
winuser.h | user32的API |
winnt.h | UNICODE字符集支持 |
Windows库文件
库文件 | 说明 |
---|---|
kernel32.dll | 提供了核心的API,例如:进程、线程、内存管理等 |
uers32.dll | 提供了窗口、消息等API |
gdi32.dll | 绘图相关的API |
2.22.2.3 SDK
SDK是指软件开发工具包(Software Development Kit),它是一组用于开发软件应用程序的工具和资源的集合。SDK包含了编写、测试和调试应用程序所需的库文件、示例代码、文档和相关工具。通过使用SDK,开发人员可以更快地开发出高质量的Windows应用程序,并且能够与操作系统和其他软件进行交互和协作。SDK通常提供了一系列API(应用程序接口),开发人员可以通过调用这些API来访问操作系统的功能和服务。
2.22.2.4 MFC
MFC全称Microsoft Foundation Class(微软基础类库),是微软公司提供的一套C++编程框架,用于Windows操作系统上的图形用户界面(GUI)应用程序开发。MFC包含了一系列的类和函数,简化了应用程序的开发过程,并提供了丰富的界面元素、事件处理、与操作系统交互的功能。通过使用MFC,开发人员可以更方便地创建和管理窗口、对话框、控件以及其他GUI元素,并实现各种交互效果和功能。MFC在Windows平台开发中得到广泛应用,特别适合使用C++进行项目开发的开发人员。
2.22.3 Python和C语言混合编程方案 todo
ctype模块中含有的基本类型与C语言类似,下面是几个基本的数据类型的对照:
Ctypes数据类型 C数据类型
c_char char
c_short short
c_int int
c_long long
c_float float
c_doule double
c_void_p void *
2.22.3.1 使用Python标准库ctypes
使用标准库ctypes直接调用C/C++编写的动态链接库。这是最简单易用的方案。Python程序员只要知道这些动态链接库函数的名称、参数类型与返回值类型就能简单地调用它。
而且,当你传入参数时,ctypes模块会自动地把Python的对象成为C/C++所对应的参数类型。
示例一:调用Windows的API:
# 定义参数类型与函数名称
import ctypes
from ctypes.wintypes import UINT, DWORDGetLastInputInfo = ctypes.windll.user32.GetLastInputInfoclass LASTINPUTINFO(ctypes.Structure):_fields_ = [("cbSize", UINT), ("dwTime", DWORD)]# 开始调用DLL导出的函数
def getLastInputTime_nt():info = LASTINPUTINFO()info.cbSize = ctypes.sizeof(info)info.dwTime = 0if not GetLastInputInfo(ctypes.byref(info)):raise WindowsError("")
return info.dwTimeprint(getLastInputTime_nt())
17230812
在这里展示了如何构造Windows的API所需要的结构体,如何填充结构体并分析返回值。
ctypes还能将Python函数提供给C/C++代码作为回调函数。
方案优势
:
不需要程序员熟悉C/C++语言,不需要安装一个C/C++的编译器,它通过操作系统的接口直接操作C/C++代码。而且ctypes是标准库的一部分,只要安装了Python就可以直接使用。
方案劣势
:
1、ctypes不能简单调用C++程序,因为C++在编译的时候使用了name mangling这个技术来实现函数的重载。C++会自动地为类的成员函数加上类名前缀。
2、对于list, set之类的数据类型,ctypes不能识别并自动地在Python与C/C++数据类型之间转换。C/C++部分不能识别Python数据类型,这时候只能用Python语言来编写转换代码。如果数据量较大,或者调用很频繁,转换代码反而会浪费很多的资源。
示例二:调用Windows动态库中的函数删除文件
from ctypes import windlldll = windll.LoadLibrary("Kernel32.dll")
dll.DeleteFileW('1.txt')
示例三:将Python数据类型显示转换成C格式
import ctypes
from icecream import icnum = ctypes.c_int(0)
ic(num, type(num))
16:51:50|> num: c_long(0), type(num): <class ‘ctypes.c_long’>
示例四:调用windows的API弹出对话框
from ctypes import windlldll = windll.LoadLibrary("user32.dll")
dll.MessageBoxW(0, '内容', '标题', 0)
2.22.3.2 使用Cython
使用Cython语言,一种类似于Python语言的一种新型语言编写预定功能的代码,然后将这些代码转换成为C语言编译成为Python语言可以直接调用的二进制模块。Cython语言是融合Python语言与C语言的一种新型语言。它本身能够理解Python语言的语法,然后在其基础上增加了某些C语言的语法,以便更精细地控制数据类型与指针。基本兼容Python语法是这个解决方案最大的特点。很多时候,Python程序员只要在旧的代码中简单地声明一下代码中所使用的参数、变量的类型,就能把立即为旧的Python程序提速。
Cython提供了一个名为pyximporter的工具,能够在安装了C/C++编译器的计算机上面为简单的Cython程序直接生成相应的Python模块。这使得Cython的使用与普通的Python程序一样简单。比如下面这段代码,直接保存为myhello.pyx即可被调用。
#myhello.pyx
def sayHelloTenTimes():cdef int i #只要简单地为变量标识类型即可加速循环。for i in range(0, 10):print("hello, world!")
$ python
import pyximport; pyximport.install()
import myhello
myhello.sayHelloTenTimes()
由此可见,Cython非常容易使用。而且不仅能够处理C语言的模块,还能处理C++的模块——虽然没有直接支持虚函数之类的完整C++特性。因为它不直接使用C/C++语法,而是另外设计比C/C++更简洁优雅的新型语法,因此,对于不熟悉C/C++的程序员来说有很大的吸引力。相比ctypes来说,因为参数类型转换更加智能与高效,所以通常能够提升更多的效率。
劣势呢,所谓用Python程序员所熟练的语法来编写高速的运算代码,乍一听相当地有吸引力。但是如果想要更深入地控制内存与数据结构时,程序员可能会发现,现在他不得不熟练地掌握C/C++语言,然后用Cython的语法写出来。以程序员们懒惰的性格,这反而是件难以忍受的事件。这或许是Cython本身并不大流行的主要原因吧。
2.22.3.3 使用boost.python
使用boost.python。有意思的是,与ctypes/Cython形成鲜明的对比,boost.python倾向于让C++程序员拥有更熟悉的编程环境。它让C++程序员使用他所熟悉的C++语法直接控制Python的数据结构,调用Python的解释器。它没有像Cython那样发明新的语法,而是直接使用C++的语法,编写供Python使用的接口。与Cython同样的道理,它的效率优胜于ctypes。
与Cython/SWIG/SIP等方案相比,程序员只需要学习C/C++与Python两种语言。另外,与本文提到的几种解决方案相比,它非常适合在主要由C++编写的程序中控制Python代码。不仅功能更强大、效率还更高。如此神奇的解决方案会有什么劣势呢?某些人可能不同意吧,老鱼一听说它依赖于boost就蔫了,感觉编译与学习庞大又奇怪的boost非常浪费生命。
2.22.3.4 使用SWIG或者SIP
使用SWIG或者SIP,通过编写一个接口文件,使用类似于C/C++语法——声明函数、类型的信息,然后使用特殊的工具为C/C++的代码生成Python的接口代码。这些接口代码能够在Python与C/C++之间的数据结构转换。最终编译这些接口代码,成为Python的二进制模块。SWIG与SIP的接口文件与C/C++的头文件非常相似。
这两种工具差不多,因为。本质上,他们都与Cython类似,都使用了中间语言来生成转换代码。但SWIG/SIP能够在他们的接口文件中嵌入C/C++,能够让程序员仔细地调节数据类型的转换过程。在使用上,它比Cython的层次更低,更接近于Python本身提供的API。
SWIG能够为多种脚本语言生成转换代码。而SIP则专门针对Python与C++。此外,SIP本身是作为PyQt的专门工具来开发的,因此它能够理解Qt的signal/slot。从应用项目上来看,SWIG似乎会更广泛一点。而SIP,目前所见的项目基本都与PyQt相关。据说SWIG对于C++的支持不好,不知道有没有人来说一下呢。相比之下,SIP对于C++的支持非常完善,诸如虚函数、protected member function、模版、析构函数、异常等特性都得到良好的支持。而且SIP支持Python的GIL,还拥有一个使用Python编写的编译系统。可能会更方便一点。
然而这种方案毕竟要学习一种新的语言,所以从表面上来看不如Cython和boost.python讨喜。当程序员想要仔细地调节类型转换代码的时候,需要学习SWIG/SIP的内部机制,被限定使用特殊的变量名。这使得这种方案的学习曲线相对较高。
2.22.3.5 直接使用Python的API
直接使用Python的API,可以称之为最终解决方案。Cython, SWIG, SIP的接口文件转换后所生成的C/C++代码实际上都使用Python的API。与其它方案相比,这种方案相当地繁复,必须为每次函数调用编写数据转换代码,还要操心Python对象的引用计数。我觉得这种方案一无是处,这时就不再多讲了。其它的工具pybindgen不知道什么情况。有兴趣的话可以看看。