• cython教程


    1.安装
    http://cython.org/release/Cython-0.22.1.tar.gz
    python setup.py install
    2.写测试代码: zhouhh@zhouhh-home:~$ vi test.pyx [python] view plaincopy def sayhello(char* str): if str == None: print 'hello world' else: print str 3.编译成C语言 zhouhh@zhouhh-home:~$ cython test.pyx zhouhh@zhouhh-home:~$ ls test.c test.c test.c是由cython通过 test.pyx生成的。 4. gcc编译成.o文件 直接执行gcc会出错,必须包含python目录。 zhouhh@zhouhh-home:~$ gcc test.c test.c:4:20: error: Python.h: 没有该文件或目录 test.c:5:26: error: structmember.h: 没有该文件或目录 test.c:7:6: error: #error Python headers needed to compile C extensions, please install development version of Python. ... zhouhh@zhouhh-home:~$ gcc -c -fPIC -I/usr/include/python2.6 test.c -fPIC表示编译成共享库,-I后跟include的路径。 5.生成共享库 zhouhh@zhouhh-home:~$ gcc -shared test.o -o test.so 6.在python中引用共享库 zhouhh@zhouhh-home:~$ vi test.py import test test.sayhello('ni hao') 7.执行 zhouhh@zhouhh-home:~$ python test.py ni hao 8.疑问: 不知怎么穿NULL指针给函数参数。 9.参考: http://www.cython.org/ http://blog.csdn.net/lanphaday/archive/2009/09/17/4561611.aspx
    一种为Python写C扩展的方式,尝试一下。
    参考文献:
        [r] 官方主页: http://www.cython.org/
        [r] Cython三分钟入门: http://blog.csdn.net/lanphaday/archive/2009/09/17/4561611.aspx
        [u] A quick Cython introduction: http://www.perrygeo.net/wordpress/?p=116 其实就是上文的原文
        [i] Cython's Documentation: http://docs.cython.org/ 看到"Extensioin types"
    
    2   基 本使用
    Cython基于pyrex,但是拥有更多功能和优化。用来写Python的C扩展的,并生成有效的C代码。写出的文件扩展名是 ".pyx" ,已经可以算作一种语言了。
    一个简单的加法函数( addtest.pyx ):
    def addtest(a,b):
        cdef float c=a+b
        return c
    
    编译和生成动态库:
    cython addtest.pyx
    gcc -c -fPIC -I/usr/include/python2.5 addtest.c
    gcc -shared addtest.o -o addtest.so
    
    使用:
    $ python
    >>> import addtest
    >>> addtest(1,2)
    3.0
    
    构建Cython代码的方式:
        使用 setup.py ,常用
        使用pyximport导入 ".pyx" 文件
        运行cython命令编译出.c文件后再编译成.so文件
        使用Sage
    
    使用 setup.py 方式,例如一个 hello.pyx 文件,编写的 setup.py 如下:
    from distutils.core import setup
    from distutils.extension import Extension
    from Cython.Distutils import build_ext
    ext_modules=[Extension('hello',['hello.pyx'])]
    setup(
        name='Hello world app',
        cmdclass={'build_ext':build_ext},
        ext_modules=ext_modules
    )
    
    构建使用命令 python setup.py build_ext --inplace 。
    Cython提高速度的主要原因是使用静态类型。可以在任何参数前直接使用C的类型定义。函数内的话要加"cdef"前缀。如:
    def f(double x):
        cdef double ret
        ret=x**2-x
        return ret
    
    仅仅使用Cython编译纯Python代码可以提高35%的性能,几乎全部使用静态类型以后提高4倍。
    
    C风格函数声明,"except? -2"表示返回-2时就是出错了。不过"except *"是肯定安全的。如:
    cdef double f(double) except? -2:
        return x**2-x
    使用cpdef时,这个函数就可以同时被C和Python调用了。当使用了C函数时,因为避开了昂贵的函数调用,旺旺可以提高150倍的速度。
    不要过度优化,一步步的优化并且查看profile。使用"cython -a"参数可以查看HTML报告。
    
    3   调 用其他C库
    3.1   简 单例子
    导入"math.h"中的 sin() 函数并使用:
    cdef extern from "math.h":
        double sin(double)
    cdef double f(double x):
        return sin(x*x)
    Cython不会去扫描头文件,所以自己必须再声明一遍。下面是使用时必须连接上其他库的 setup.py 文件:
    from distutils.core import setup
    from distutils.extension import Extension
    from Cython.Distutils import build_ext
    ext_modules=[
        Extension('demo',['demo.pyx',],libraries=['m',])
        ]
    setup(
        name='Demos',
        cmdclass={'build_ext':build_ext},
        ext_modules=ext_modules,
    )
    同理可以使用任何动态或静态编译的库。
    3.2   重 新定义外部C库的定义
    一段C代码,头文件中的类型定义与函数声明:
    typedef struct _Queue Queue;
    typedef void *QueueValue;
    
    Queue *queue_new(void);
    void queue_free(Queue *queue);
    
    int queue_push_head(Queue *queue, QueueValue data);
    QueueValue queue_pop_head(Queue *queue);
    QueueValue queue_peek_head(Queue *queue);
    
    int queue_push_tail(Queue *queue, QueueValue data);
    QueueValue queue_pop_tail(Queue *queue);
    QueueValue queue_peek_tail(Queue *queue);
    
    int queue_is_empty(Queue *queue);
    
    对应的Cython定义,写入一个".pxd"文件中:
    cdef extern from "libcalg/queue.h":
        ctypedef struct Queue:
            pass
        ctypedef void* QueueValue
    
        Queue* new_queue()
        void queue_free(Queue* queue)
    
        int queue_push_head(Queue* queue, QueueValue data)
        QueueValue  queue_pop_head(Queue* queue)
        QueueValue queue_peek_head(Queue* queue)
    
        int queue_push_tail(Queue* queue, QueueValue data)
        QueueValue queue_pop_tail(Queue* queue)
        QueueValue queue_peek_tail(Queue* queue)
    
        bint queue_is_empty(Queue* queue)
    
    大部分时候这种声明与头文件几乎是一样的,你可以直接拷贝过来。唯一的区别在最后一行,C函数的返回值其实是布尔值,所以用bint类型会转换成 Python的布尔值。
    这里可以不关心结构体的内容,而只是用它的名字。
    
    4   类 定义
    一个类的例子:
    cimport cqueue
    cimport python_exc
    
    cdef class Queue:
        cdef cqueue.Queue_c_queue
        def __cinit__(self):
            self._c_queue=cqueue.new_queue()
    
    这里的构造函数是 __cinit__() 而不是 __init__() 。虽然 __init__() 依然有效,但是并不确保一定会运行(比如子类忘了调用基类的构造函数)。因为未初始化的指针经常导致Python挂掉而没有任何提示,所以 __cinit__() 总是会在初始化时调用。不过其被调用时,对象尚未构造完成,所以除了cdef字段以外,避免其他操作。如果要给 __cinit__() 构造和函数加参数,必须与 __init__() 的匹配。
    
    构造函数初始化资源时记得看看返回的资源是否是有效的。如果无效可以抛出错误。Cython提供了内存不足异常,如下:
    
    def __cinit__(self):
        self._c_queue=cqueue.new_queue()
        if self._c_queue is NULL:
            python_exc.PyErr_NoMemory()
    
    Cython提供的析构函数,仅在建立成功内部对象时释放内部对象:
    
    def __dealloc__(self):
        if self._c_queue is not NULL:
            cqueue.queue_free(self._c_queue)
    
    将数据以通用指针方式进入,和返回时的强制类型转换:
    cdef append(self,int value):
        cqueue.queue_push_tail(self._c_queue,<void*>value)
    cdef int pop(self):
        return <int>cqueue.queue_pop_head(self._c_queue)
    
    Cython除了支持普通Python类以外,还支持扩展类型,使用"cdef class"定义。在内存占用和效率上更好。因为使用C结构体存储字段和方法,而不是Python字典。所以可以存储任意C字段类型,而不是其 Python包装。访问时也是直接访问C的值,而不是通过字典查找。
    
    普通的Python类可以继承自cdef类,但是反之则不行。Cython需要知道完整的继承层次来定义C结构体,并且严格限制单继承。不过普通 Python类可以继承任一数量的Python类和扩展类型,无论在Python中还是在Cython代码中。
    
    5   与 Python交互
    如果Cython调用Python函数失败,则直接返回NULL,而不是异常对象。
    如果一个函数既有可能返回NULL,也有可能返回0,则处理起来就比较麻烦。Python C API的做法是 PyErr_Occurred() 函数。不过这种方式有性能损耗。在Cython中你可以自己指定哪个返回值代表错误,所以环境只要检查这个返回值即可。其他所有值都回无损耗的被接受。
    在函数定义时指定except子句,则仅在函数返回该值时检查是否需要抛出异常。这样同一个函数返回0和返回0同时返回错误就可以区分开。例子:
    cdef int pop(self) except? 0:
        #...
    类中的 cdef 定义C方法,而 cpdef 可以同时定义C方法和Python方法
    举个例子:
    # hello.pyx
    def say_hello_to(name):
    print("Hello %s!" % name)
    
    # setup.py
    from distutils.core import setup
    from distutils.extension import Extension
    from Cython.Distutils import build_ext
    
    ext_modules = [Extension("hello", ["hello.pyx"])]
    
    setup(
    name = 'Hello world app',
    cmdclass = {'build_ext': build_ext},
    ext_modules = ext_modules
    )
    
    在命令行里执行编译:
    python setup.py build_ext --inplace
    
    在Python里调用:
    from hello import say_hello_to
    say_hello_to(" world ")
  • 相关阅读:
    关于在组件GIS开发中使用Python的一点补充说明
    shell环境变量以及set,env,export的区别
    快速配置 Samba 将 Linux 目录映射为 Windows 驱动器
    Expect 教程中文版
    rpm 包管理
    .bash_profile和.bashrc的什么区别
    grep 零宽断言
    自动化测试
    dialog shell下的gui设计 代替繁杂libncurses编程
    x11 gtk qt gnome kde 之间的区别和联系
  • 原文地址:https://www.cnblogs.com/timssd/p/4706240.html
Copyright © 2020-2023  润新知