- 浏览: 962825 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
wuchanghyz:
我的情况恰巧与博主相反,做了不少电子开发,总是无法把电子硬件与 ...
软件工程师如何转行做电子 -
kaopuso:
学习了 谢谢
扩展和嵌入Python解释器 Extending and Embedding the Python Interpreter -
YA_AY:
好厉害!
2014年度笔记统计与2015设想 -
cwqcwqmax9:
我也很想把底层的电路原理搞懂啊
软件工程师如何转行做电子 -
phrmgb:
团队建设是为了建设团队,感觉不能光吃,吃的过程中增加些互动游戏 ...
团队小论-自助餐
ctypes-Python外部函数库
译者注:翻译的并不完全,不过对于大多数应用是足够了。总体感觉使用ctypes还是比较麻烦,需要自己重新把头文件给用Python写一遍。再者就是对于指针的指针一类还不知怎么实现。结论是我不打算再用ctypes,还是原生的扩展模块靠谱。发出来给能用的上的人用吧。
gashero |
2009-11-02 |
从Python2.5开始引入。
ctypes是Python的外部函数库。它提供了C兼容的数据类型,并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。
1 ctypes入门
本入门中的代码使用doctest确保可用。不过一些代码在linux/windows/mac os x中的行为可能略有差异,这在其doctest的注释中有所表示。
少数代码示例引用了ctypes的c_int类型。这个类型是32bit系统中c_long类型的别名。所以你在期待c_int而显示c_long时不必疑惑,他们是一样的。
1.1 载入动态链接库
ctypes导出了 cdll ,在windows上还有 windll 和 oledll 对象用于载入动态链接库。
载入动态链接库可以直接存取其属性。 cdll 载入导出函数符合cdecl调用规范的库,而 windll 载入导出函数符合 stdcall 调用规范的库, oledll 也使用 stdcall 调用规范,并假设函数返回Windows的HRESULT错误码。错误码用于在出错时自动抛出WindowsError这个Python异常。
如下是Windows的例子,主意msvcrt是MS标准C库,包含了大部分标准C函数,并且使用cdecl调用规范:
>>> from ctypes import * >>> print windll.kernel32 <WinDLL 'kernel32', handle ... at ...> >>> print cdll.msvcrt <CDLL 'msvcrt', handle ... at ...> >>> libc=cdll.msvcrt >>>
Windows通常使用".dll"作为动态链接库的扩展名。
Linux上需要指定包含扩展名的文件名来载入动态库,所以属性存取方式就失效了。你可以使用 LoadLibrary 方法,或者创建CDLL的实例来载入:
>>> cdll.LoadLibrary("libc.so.6") <CDLL 'libc.so.6', handle ... at ...> >>> libc==CDLL("libc.so.6") >>> libc <CDLL 'libc.so.6', handle ... at ...> >>>
1.2 从载入的动态链接库中存取函数
函数是作为dll对象的属性来存取的:
>>> from ctypes import * >>> libc.printf <_FuncPtr object at 0x...> >>> print windll.kernel32.GetModuleHandleA <_FuncPtr object at 0x...> >>> print windll.kernel32.MyOwnFunction Traceback (most recent call last): File "<stdin>", line 1, in ? File "ctypes.py", line 239, in __getattr__ func = _StdcallFuncPtr(name,self) AttributeError: function 'MyOwnFunction' not found >>>
主意win32系统动态链接库,如kernel32和user32经常同时导出ANSI和UNICODE版本的函数。UNICODE版本的会在名字末尾加"W",而ANSI版本的加上"A"。Win32版本的 GetModuleHandle 函数,返回给定模块名的句柄,有如下C原型,还有一个宏用于暴露其中一个作为 GetModuleHandle ,依赖于UNICODE定义与否:
/* ANSI version */ HMODULE GetModuleHandleA(LPCSTR lpModuleName); /* UNICODE version */ HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll 并不会自动选择调用某个版本,所以你必须指定要调用的,传递的时候也要指定正确的字符串参数类型。
有时动态链接库导出函数并不是有效的Python标识符,例如 "??2@YAPAXI@Z" 。这种情况下,你必须使用 getattr 获取函数:
>>> getattr(cdll.msvcrt,"??2@YAPAXI@Z") <_FuncPtr object at 0x...> >>>
在Windows上,有些动态链接库导出函数不是用名字,而是用序号(ordinal)。这些函数通过索引存取:
>>> cdll.kernel32[1] <_FuncPtr object at 0x...> >>> cdll.kernel32[0] Traceback (most recent call last): File "<stdin>", line 1, in ? File "ctypes.py", line 310, in __getitem__ func = _StdcallFuncPtr(name,self) AttributeError: function ordinal 0 not found >>>
1.3 调用函数
你可以像正常的Python函数一样调用这些函数。这里用 time() 函数示例,返回Unix epoch系统时间,和 GetModuleHandleA() 函数,返回win32模块句柄。
这个例子调用函数时附带NULL指针(None作为NULL指针):
>>> print libc.time(None) 1150640792 >>> print hex(windll.kernel32.GetModuleHandleA(None)) 0x1d000000 >>>
在调用函数时,如果使用了错误的参数数量和调用规范时,ctypes尝试保护调用。不幸的是该功能仅在Windows上有用。它通过检查函数返回栈来实现,所以尽管发生了错误,但是函数还是调用了:
>>> windll.kernel32.GetModuleHandleA() Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: Procedure probably called with not enough argument (4 bytes missing) >>> windll.kernel.GetModuleHandleA(0,0) Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: Procedure probably called with too many argument (4 bytes in excess)
这在你使用了错误的调用规范时同样会发生:
>>> cdll.kernel32.GetModuleHandleA(None) # doctest: +WINDOWS Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: Procedure probably called with not enough arguments (4 bytes missing) >>> >>> windll.msvcrt.printf("spam") # doctest: +WINDOWS Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: Procedure probably called with too many arguments (4 bytes in excess) >>>
想要找到正确的调用规范,你必须查看C头文件或者函数的文档。
在Windows,ctypes使用win32结构异常处理,避免无保护的挂掉:
>>> windll.kernel32.GetModuleHandleA(32) Traceback (most recent call last): File "<stdin>", line 1, in ? WindowsError: exception: access violation reading 0x00000020
尽管如此,仍然有很多方法用ctypes挂掉Python,所以你必须很小心的使用。
None、整数、长整数、字节串和unicode字符串是可以作为本地Python对象直接传递给函数调用的。None是作为C的NULL指针,字 节串和unicode字符串作为内存块指针传递(char* 或 wchar_t*)。Python整数和长整数作为平台相关的C类型传递。
在调用更多的函数之前,必须了解关于ctypes数据类型的知识。
1.4 基本数据类型
ctypes定义了一系列基本C数据类型:
ctypes类型 | C类型 | Python类型 |
c_char | char | 1个字符的字符串 |
c_wchar | wchar_t | 1个字符的unicode字符串 |
c_byte | char | int/long |
c_ubyte | unsigned char | int/long |
c_short | short | int/long |
c_ushort | unsigned short | int/long |
c_int | int | int/long |
c_uint | unsigned int | int/long |
c_long | long | int/long |
c_ulong | unsigned long | int/long |
c_longlong | __int64 或 long long | int/long |
c_ulonglong | unsigned __int64 或 unsigned long long | int/long |
c_float | float | float |
c_double | double | float |
c_char_p | char * (NUL结尾字符串) | string或None |
c_wchar_p | wchar_t * (NUL结尾字符串) | unicode或None |
c_void_p | void * | int/long或None |
所有这些类型都可以通过调用可选传输初始化值方式指定值:
>>> c_int() c_long(0) >>> c_char_p("Hello, world") c_char_p('Hello, world') >>> c_ushort(-3) c_ushort(65533) >>>
这些类型都是可变的,其值也是随后可变的:
>>> i=c_int(42) >>> print i c_long(42) >>> print i.value 42 >>> i.value=-99 >>> print i.value -99 >>>
对指针类型 c_char_p/c_wchar_p/c_void_p 的赋值将会改变其指向的内存区域地址,而不是改变内存块的值(当然了,因为Python字符串是只读的):
>>> s="Hello, world" >>> c_s=c_char_p(s) >>> print c_s c_char_p('Hello, world') >>> c_s.value="Hi, there" >>> print c_s c_char_p('Hi, there') >>> print s #第一个字符串没有改变 Hello, world >>>
必须小心的是,不要传递这些的指针给可变内存。如果你需要可变内存块,ctypes提供了 create_string_buffer() 函数。当前内存块可以存取或改变,如果你想要将其作为NUL结尾字符串方式,使用值的方法:
>>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print sizeof(p), repr(p.raw) 3 '\x00\x00\x00' >>> p = create_string_buffer("Hello") # create a buffer containing a NUL terminated string >>> print sizeof(p), repr(p.raw) 6 'Hello\x00' >>> print repr(p.value) 'Hello' >>> p = create_string_buffer("Hello", 10) # create a 10 byte buffer >>> print sizeof(p), repr(p.raw) 10 'Hello\x00\x00\x00\x00\x00' >>> p.value = "Hi" >>> print sizeof(p), repr(p.raw) 10 'Hi\x00lo\x00\x00\x00\x00\x00' >>>
create_string_buffer() 函数已经替换了 c_buffer() 函数(仍然作为别名存在),有如 c_string() 函数以前,只是出现在以前的版本中。想要创建包含unicode字符(对应C类型wchar_t)的可变内存块,使用 create_unicode_buffer() 函数。
1.5 调用函数,继续
需要注意的是,printf打印到真实的标准输出,而不是 sys.stdout ,所以这些例子仅在控制台模式有效,而不是IDLE或PythonWin:
>>> printf=libc.printf >>> printf("Hello, %s\n","World!") Hello, World! 14 >>> printf("Hello, %S", u"World!") Hello, World! 13 >>> printf("%d bottles of beer\n", 42) 42 bottles of beer 19 >>> printf("%f bottles of beer\n", 42.5) Traceback (most recent call last): File "<stdin>", line 1, in ? ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 >>>
有如前面所说,除了整数、字符串和unicode字符串以外的Python类型必须使用ctypes类型做包装,所以他们可以转换为必须的C数据类型:
>>> printf("An int %d, a double %f\n",1234,c_double(3.14)) Integer 1234, double 3.1400001049 31 >>>
1.6 使用自定义数据类型调用函数
你可以使用自定义ctypes参数转换,允许你自己的类作为函数参数。ctypes寻找对象的 _as_parameter_ 属性,并将其作为函数参数。当然,必须是整数、字符串或unicode
>>> class Bottles(object): ... def __init__(self, number): ... self._as_parameter_ = number ... >>> bottles = Bottles(42) >>> printf("%d bottles of beer\n", bottles) 42 bottles of beer 19 >>>
如果你不想存储实例的数据到 _as_parameter_ 实例变量,你可以定义一个属性确保数据有效。
1.7 指定必须的参数类型(函数原型)
可以通过指定函数的 argtypes 属性来指定函数的参数类型。
argtypes必须是一个C数据类型序列(printf函数在这里不是个好例子,因为它需要依赖于格式化字符串的可变数量和多种类型的参数,反过来说倒是很适合于练手):
>>> printf.argtypes=[c_char_p,c_char_p,c_int,c_double] >>> printf("String '%s', Int %d, Double %f\n","Hi",10,2.2) String 'Hi', Int 10, Double 2.200000 37 >>>
指定不兼容的参数类型,和尝试转换参数到到无效类型会出错:
>>> printf("%d %d %d", 1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in ? ArgumentError: argument 2: exceptions.TypeError: wrong type >>> printf("%s %d %f", "X", 2, 3) X 2 3.00000012 12 >>>
如果你自定义的类要传递给函数调用,必须实现 from_param 类方法,才能在argtypes序列中使用。 from_param 类方法接收Python对象传递到函数调用,需要做类型检查或者其他确保对象可以被接受的工作,然后返回对象本身, _as_parameter_ 属性,或者你想要传递给C函数的参数。再次说明,返回结果必须是整数、字符串、unicode、ctypes实例,或者任何有 _as_parameter_ 属性的东西。
1.8 返回类型
缺省情况假设函数返回C的int类型。其他返回类型可以通过设置函数的 restype 属性来实现。
这里是一个更高级的例子,它使用strchr函数,需要一个字符串指针和一个字符,返回字符串的指针:
>>> strchr = libc.strchr >>> strchr("abcdef", ord("d")) # doctest: +SKIP 8059983 >>> strchr.restype = c_char_p # c_char_p is a pointer to a string >>> strchr("abcdef", ord("d")) 'def' >>> print strchr("abcdef", ord("x")) None >>>
如果你想要上面的 ord("x") 调用,你可以设置argtypes属性,而第二个参数的Python字符串会转换成C字符:
>>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr("abcdef", "d") 'def' >>> strchr("abcdef", "def") Traceback (most recent call last): File "<stdin>", line 1, in ? ArgumentError: argument 2: exceptions.TypeError: one character string expected >>> print strchr("abcdef", "x") None >>> strchr("abcdef", "d") 'def' >>>
你还可以使用Python的可调用对象(函数或者类的例子)作为restype属性,如果外语函数返回整数。这时在C函数调用结束后会使用其返回的 整数调用这个Python可调用对象,而返回值作为函数调用的返回值。相当于对C函数返回值做了包装。这对于检查错误码而抛出异常的情况非常有用:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA # doctest: +WINDOWS >>> def ValidHandle(value): ... if value == 0: ... raise WinError() ... return value ... >>> >>> GetModuleHandle.restype = ValidHandle # doctest: +WINDOWS >>> GetModuleHandle(None) # doctest: +WINDOWS 486539264 >>> GetModuleHandle("something silly") # doctest: +WINDOWS Traceback (most recent call last): File "<stdin>", line 1, in ? File "<stdin>", line 3, in ValidHandle WindowsError: [Errno 126] The specified module could not be found. >>>
这里的 WinError 是一个函数,会调用Windows的 FormatMessage() API来获取错误码的字符串描述,并且返回异常。 WinError 接受可选的错误码参数,如果没有指定则调用 GetLastError() 获取。
需要注意的是强大的错误检查机制是通过 errcheck 属性实现的。具体查看手册了解细节。
1.9 传递指针(或者传递参数引用)
有时C函数需要一个指针指向的数据作为参数,还有可能是想向里面写的位置,或者数据太大不适合传递。这也叫做传递参数引用。
ctypes导出 byref() 函数用于传递参数引用。同样也可以用于指针函数,尽管指针对象可以做很多工作,但是如果你并不需要在Python中使用指针对象的话,使用 byref() 会更快:
>>> i = c_int() >>> f = c_float() >>> s = create_string_buffer('\000' * 32) >>> print i.value, f.value, repr(s.value) 0 0.0 '' >>> libc.sscanf("1 3.14 Hello", "%d %f %s", ... byref(i), byref(f), s) 3 >>> print i.value, f.value, repr(s.value) 1 3.1400001049 'Hello' >>>
1.10 结构和联合
结构和联合必须继承自ctypes模块的 Structure 和 Union 类。每个子类必须定义 _fields_ 属性,该属性必须是2元素元组的列表,包含字段名和字段类型。
字段类型必须是ctypes类型,例如 c_int ,或者其他派生的ctypes类型:结构、联合、数组、指针。
这里有个POINT结构体的简单例子,包含两个整数叫做x和y,同时展示了如何构造结构体:
>>> from ctypes import * >>> class POINT(Structure): ... _fields_ = [("x", c_int), ... ("y", c_int)] ... >>> point = POINT(10, 20) >>> print point.x, point.y 10 20 >>> point = POINT(y=5) >>> print point.x, point.y 0 5 >>> POINT(1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: too many initializers >>>
你还可以构造跟多复杂的结构体。结构体可以自包含作为一个字段类型。
@waiting ...
1.11 结构/联合对齐和字节序
默认情况下结构体和联合的对齐使用C编译器相同的方式。这可以通过 _pack_ 类属性来重载其行为。这必须设置一个正数指定字段的最大对齐。这个功能与MSVC中的 #pragma pack(n) 功能一样。
ctypes中的结构体和联合使用本地字节序。想要用非本地字节序,可以使用 BigEndianStructure 、 LittleEndianStructure 、 BigEndianUnion 、 LittleEndianUnion 基类。这些类无法包含指针字段。
1.12 结构与联合中的位字段
@waiting ...
1.13 数组
数组就是序列,包含固定数量(fixed number of)的相同类型的实例。
推荐的创建数组类型的方式是使用正数和乘号应用到类型:
TenPointsArrayType=POINT*10
这里有个巧妙的例子,一个结构体包含一个字段有4个POINT:
>>> from ctypes import * >>> class POINT(Structure): ... _fields_ = ("x", c_int), ("y", c_int) ... >>> class MyStruct(Structure): ... _fields_ = [("a", c_int), ... ("b", c_float), ... ("point_array", POINT * 4)] >>> >>> print len(MyStruct().point_array) 4 >>>
@waiting ...
1.14 指针
指针实例使用 pointer() 函数:
>>> from ctypes import * >>> i=c_int(42) >>> pi=pointer(i) >>>
指针实例有一个 contents 属性返回指针指向的内容对象,例如上面的例子:
>>> pi.contents c_long(42) >>>
注意ctypes没有OOR(Original Object Return原始对象返回),他在你请求一个属性时构造一个新的、等同的对象:
>>> pi.contents is i False >>> pi.contents is pi.contents False >>>
@waiting ...
发表评论
-
扩展和嵌入Python解释器 Extending and Embedding the Python Interpreter
2015-07-31 14:06 130142007年开始使用Python与C的交互编程,那时分享了一 ... -
PyCon2014北京场演讲《Python与机器人技术》
2014-11-18 01:05 16951Python与机器人技术 此 ... -
Python的Daemon管理器-zdaemon
2010-12-03 00:17 7296Python的Daemon管理器- ... -
果壳网(guokr.com)发布了
2010-11-30 15:55 6193果壳网(guokr.com)发布了 到科学松鼠会已经快 ... -
Cython应用手记
2010-04-21 15:47 24494Cython应用手记 作者: gash ... -
在Python中用函数式编程来恶心人
2009-09-17 14:38 6300好友caoshijun最近找我做一个字符串的解码,字符串来源于 ... -
eventlet做wsgi服务器的例子
2009-08-10 16:29 6251eventlet做wsgi服务器的例子 from e ... -
协程才是未来-性能夸张的协程服务器,基于eventlet(greenlet)的http性能测试
2009-08-07 11:21 21001早上好友baojing.jiao发来消息跟我讨论eventle ... -
如何鄙视ez_setup和setuptools
2009-04-22 15:13 5676如何鄙视ez_setup和setuptools 如两个 ... -
getopt-解析命令行参数
2009-03-12 10:40 4661getopt-解析命令行参数 译者: g ...
相关推荐
它是一个外部函数库,为Python提供了与C语言兼容的数据类型,并允许Python代码调用动态链接库(DLLs)或共享库中的函数。通过ctypes模块,可以将这些库封装在纯Python代码中,无需编写额外的C代码。 标签“python c...
`pbkdf2_ctypes`库使用了CTYPES,这是一个Python的外部库接口,允许Python代码直接调用C语言编写的动态链接库(DLLs或.so文件),从而实现了高效的PBKDF2算法。 在Python中,`pbkdf2_ctypes`库提供了简单易用的API...
Python 的 ctypes 库提供了一个简单的方式来调用外部 DLL 中的函数。ctypes 库提供和 C 语言兼容的数据类型,可以很方便地调用 C DLL 中的函数。在 Python 2.5 官方安装包中都带有 ctypes 1.1 版本。 ctypes 库的...
ctypes 模块是 Python 的一个外部扩展库,提供了一个强大的工具来调用动态链接库(DLL)。本文档将详细介绍 ctypes 模块的使用方法和相关知识点。 一、加载动态链接库 ctypes 模块可以加载动态链接库,导出 cdll ...
12. **系统接口**:Python的ctypes库可以调用C语言编写的动态链接库,让Python脚本能够访问底层操作系统接口。 13. **日志和监控**:syslog、sentry等工具或库可以实现日志记录和错误监控,对系统状态进行实时跟踪...
Python的ctypes模块是Python与C语言库交互的重要工具,它允许Python程序调用C编写的动态链接库(DLL)中的函数。ctypes提供了一种简单的方法,将Python对象转换为C数据类型,并且可以调用C库中的函数,使得Python...
`ctypes` 是Python标准库中的一个重要模块,它提供了一系列与C兼容的数据类型,并支持调用外部C语言编写的动态链接库(DLL)或共享库(SO)。通过这种方式,可以将已有的C代码无缝集成到Python程序中,或者编写C语言...
这个库充分利用了`ctypes`模块,使得它能够在不依赖外部库的情况下,直接调用操作系统底层的API来捕获屏幕图像。`ctypes`是Python的标准库之一,用于调用C语言编写的动态链接库(DLLs on Windows或SOs on Unix-like ...
它提供了一个高级的外部函数接口,允许Python调用C编译的静态和动态库。ctypes允许Python程序创建、访问和操作C语言的数据类型,如整型、浮点型、指针等。此外,ctypes可在多种操作系统上运行,如Windows、MacOSX、...
这个库通过Python的ctypes模块与C库交互,实现了与C版本相同的功能。使用Python库时,开发者可以利用Python的语法优势,编写更加简洁、可读性强的代码。例如,可以使用`wiringpi.GPIO.setmode(GPIO.BOARD)`来设置...
ctypes是Python的外部函数库,它提供了与C语言的接口。我们可以使用ctypes来调用操作系统的API,从而加载Shellcode到内存中。 为了绕过杀软的检测,我们可以使用加载器将Shellcode加载到内存中,然后使用Python反...
在Python编程中,有时我们需要调用外部库中的函数或功能,特别是那些由C/C++编写的库。这些库通常被编译成动态链接库(Dynamic Link Library,简称DLL)的形式。Python通过`ctypes`库提供了与这些DLL交互的能力,...
在`main.py`中,你需要先导入ctypes,然后使用`cdll.LoadLibrary()`函数加载`libhello.so`,之后就可以像调用Python函数一样调用库中的函数了。 `CMakeLists.txt`是CMake构建系统的配置文件,用于自动化编译和链接`...
CTypes 提供了高级外部函数接口,允许 Python 程序直接调用 C 库中的函数,并处理 C 语言的数据类型。CTypes 支持多种操作系统平台,如 Windows、Mac OS X、Linux 等。 使用 CTYPES 整合 Python 和 C 的过程主要...
`ctypes`是Python的一个标准库,用于调用C语言编写的函数。它允许Python程序直接调用共享库或DLL文件中的函数,无需任何额外的包装或转换。 #### 三、调用流程解析 根据医保中心提供的文档,调用流程大致分为以下...
3. **封装外部库**:将现有的C/C++库包装成Python可调用的接口,方便在Python项目中使用。 4. **跨平台开发**:cffi可以跨平台工作,使得Python应用能在多种操作系统上运行。 总之,`cffi`是Python与C库之间的一座...
- 外部接口:通过Python的ctypes或pybind11库,atomtools与C/C++等低级语言的库进行交互,实现高性能计算。 - 科学计算:利用SciPy和Matplotlib等科学计算库进行数值计算和可视化,提供直观的图形输出。 4. **...
1. 使用`ctypes`库:`ctypes`是Python的外部函数库,允许Python调用C语言编写的动态链接库(DLLs)。在Windows系统中,我们可以使用`ctypes.windll.kernel32.ReadProcessMemory`和`WriteProcessMemory`函数来读写...