子类化ctypes类型

4
我想知道是否可以对各种类型的ctypes进行子类化。我尝试写了一个简单的例子,但有一件(或两件)事情我不明白。
from ctypes import *

class my_c_int(c_int):
    pass
    # def __new__(*args):
    #     print "Hello from __new__" # why is this not called?

libc = cdll.LoadLibrary("libc.dylib")
printf = libc.printf
# printf.restype = c_int # prints "14"
printf.restype = my_c_int # prints "<my_c_int object at 0x...>"
print printf("Hello, %s\n", "World!")

为什么我的my_c_int不返回与原始c_int相同的类型,即int?是否有可能这样做?如果我写print printf(...).value就可以了。是否有可能自动为my_c_int返回.value
PS:顺便问一句,为什么__new__方法没有被调用?
编辑: 考虑另一个来自文档的例子:
from ctypes import *

libc = cdll.LoadLibrary("libc.dylib")

IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
qsort = libc.qsort
qsort.restype = None

CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))

def py_cmp_func(a, b):
    print "py_cmp_func", a[0], b[0]
    return a[0] - b[0]

cmp_func = CMPFUNC(py_cmp_func)

qsort(ia, len(ia), sizeof(c_int), cmp_func)

好的,这个没问题。我的问题是:是否可以修改CMPFUNC函数接收的参数?是否可以通过使用一些自定义/子类化的ctypes类型来实现?

例如,假设我想直接传递整数(而不是指针)给py_cmp_func。 我可以这样做:

from ctypes import *

libc = cdll.LoadLibrary("libc.dylib")

IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
qsort = libc.qsort
qsort.restype = None

CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))

def auxiliary(a, b):
    return py_cmp_func(a[0], b[0])

def py_cmp_func(a, b):
    print "py_cmp_func", a, b
    return a - b

cmp_func = CMPFUNC(auxiliary)

qsort(ia, len(ia), sizeof(c_int), cmp_func)

这也可以工作,但这不是我想要的。是否有可能在没有辅助函数的情况下实现这种行为?就像:

from ctypes import *

libc = cdll.LoadLibrary("libc.dylib")

IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
qsort = libc.qsort
qsort.restype = None

class my_int_pointer(object):

    @classmethod
    def from_param(cls, obj): # not sure it `from_param` is the right place
                              # in any case, it will not be called, anyway...
        return cast(obj, POINTER(c_int))[0]

CMPFUNC = CFUNCTYPE(c_int, my_int_pointer, POINTER(c_int))

def py_cmp_func(a, b):
    print "py_cmp_func", a, b[0]
    return a - b[0]

cmp_func = CMPFUNC(py_cmp_func)

qsort(ia, len(ia), sizeof(c_int), cmp_func)

但是这并不起作用。

简而言之,我想知道如何定制 ctypes 函数的输入和输出。


在你的例子中,__new__ 会被调用(如果没有注释掉的话)。 - 101
@figs 你在哪个Python版本下运行它?我尝试了CPython 2.7.5和PyPy 2.4.0,但都没有成功。 - Ecir Hana
对于子类,getfunc 被跳过了。调用它只会返回一个普通的 Python 对象,所以为什么要费心呢?话虽如此,子类的 __new____init__ 方法也不会被调用,因此您无法自定义结果。它通过调用类型的 tp_alloc 直接分配对象,然后将结果复制到对象的缓冲区中。 - Eryk Sun
如果您有一个回调函数,它需要2个int值并返回一个int,那么请使用以下函数原型:callback_t = CFUNCTYPE(c_int, c_int, c_int)。由于c_int是一个简单的C类型,它的getfunc将被调用以创建Python函数调用的整数参数。与使用指针的qsort示例不同,这些参数不会是ctypes类型。 - Eryk Sun
1个回答

0

这不是一个完整的答案,但是我使用Python 2.7.6编写的代码可以正常工作:

from ctypes import *
class my_c_int(c_int):
    def __new__(*args):
        print 'new!'
my_c_int()   # prints 'new!'

你正在通过调用my_c_int()(即“()”部分)直接调用__new __(),这就是为什么它会打印出来的原因... - Ecir Hana
@Ecir Hana:在实例化类之前,__new__ 应该被调用吗? - Paulo Scardine
@EcirHana:你说的调用顺序是正确的,但它们只在实例化类时被调用。你需要这样做:my_c_int_instance = my_c_int(1234) - Paulo Scardine
@PauloScardine可能是这样,谢谢您的信息。无论如何,我不会调用__new__,我只是认为当我执行printf.restype = my_c_int时ctypes会这样做,所以我想尝试在那里自定义行为... - Ecir Hana
2
顺便提一下,Python装饰器是自定义可调用对象的输入和输出的惯用方式,继承对于您的使用情况来说并不是一个好的解决方案。 - Paulo Scardine
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接