开发者社区> 问答> 正文

使用ctypes访问C代码

你有一些C函数已经被编译到共享库或DLL中。你希望可以使用纯Python代码调用这些函数, 而不用编写额外的C代码或使用第三方扩展工具。

展开
收起
哦哦喔 2020-04-17 18:10:07 2363 0
2 条回答
写回答
取消 提交回答
  • 有点尴尬唉 你要寻找的东西已经被吃掉啦!

    可以参考这篇文章: https://www.baidu.com/link?url=6djl4oF9q-bjXsKIc7n5xA5eoO3YbD_ISURCaytWx9o0B2dmmEhXclgxb_R

    2020-04-17 23:59:52
    赞同 展开评论 打赏
  • 对于需要调用C代码的一些小的问题,通常使用Python标准库中的 ctypes 模块就足够了。 要使用 ctypes ,你首先要确保你要访问的C代码已经被编译到和Python解释器兼容 (同样的架构、字大小、编译器等)的某个共享库中了。 为了进行本节的演示,假设你有一个共享库名字叫 libsample.so ,里面的内容就是15章介绍部分那样。 另外还假设这个 libsample.so 文件被放置到位于 sample.py 文件相同的目录中了。
    
    要访问这个函数库,你要先构建一个包装它的Python模块,如下这样:
    
    # sample.py
    import ctypes
    import os
    
    # Try to locate the .so file in the same directory as this file
    _file = 'libsample.so'
    _path = os.path.join(*(os.path.split(__file__)[:-1] + (_file,)))
    _mod = ctypes.cdll.LoadLibrary(_path)
    
    # int gcd(int, int)
    gcd = _mod.gcd
    gcd.argtypes = (ctypes.c_int, ctypes.c_int)
    gcd.restype = ctypes.c_int
    
    # int in_mandel(double, double, int)
    in_mandel = _mod.in_mandel
    in_mandel.argtypes = (ctypes.c_double, ctypes.c_double, ctypes.c_int)
    in_mandel.restype = ctypes.c_int
    
    # int divide(int, int, int *)
    _divide = _mod.divide
    _divide.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
    _divide.restype = ctypes.c_int
    
    def divide(x, y):
        rem = ctypes.c_int()
        quot = _divide(x, y, rem)
    
        return quot,rem.value
    
    # void avg(double *, int n)
    # Define a special type for the 'double *' argument
    class DoubleArrayType:
        def from_param(self, param):
            typename = type(param).__name__
            if hasattr(self, 'from_' + typename):
                return getattr(self, 'from_' + typename)(param)
            elif isinstance(param, ctypes.Array):
                return param
            else:
                raise TypeError("Can't convert %s" % typename)
    
        # Cast from array.array objects
        def from_array(self, param):
            if param.typecode != 'd':
                raise TypeError('must be an array of doubles')
            ptr, _ = param.buffer_info()
            return ctypes.cast(ptr, ctypes.POINTER(ctypes.c_double))
    
        # Cast from lists/tuples
        def from_list(self, param):
            val = ((ctypes.c_double)*len(param))(*param)
            return val
    
        from_tuple = from_list
    
        # Cast from a numpy array
        def from_ndarray(self, param):
            return param.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
    
    DoubleArray = DoubleArrayType()
    _avg = _mod.avg
    _avg.argtypes = (DoubleArray, ctypes.c_int)
    _avg.restype = ctypes.c_double
    
    def avg(values):
        return _avg(values, len(values))
    
    # struct Point { }
    class Point(ctypes.Structure):
        _fields_ = [('x', ctypes.c_double),
                    ('y', ctypes.c_double)]
    
    # double distance(Point *, Point *)
    distance = _mod.distance
    distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point))
    distance.restype = ctypes.c_double
    如果一切正常,你就可以加载并使用里面定义的C函数了。例如:
    
    >>> import sample
    >>> sample.gcd(35,42)
    7
    >>> sample.in_mandel(0,0,500)
    1
    >>> sample.in_mandel(2.0,1.0,500)
    0
    >>> sample.divide(42,8)
    (5, 2)
    >>> sample.avg([1,2,3])
    2.0
    >>> p1 = sample.Point(1,2)
    >>> p2 = sample.Point(4,5)
    >>> sample.distance(p1,p2)
    4.242640687119285
    >>>
    
    2020-04-17 18:10:17
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载