cython简单入门

1|0cython

Cython是一个编程语言,它通过类似Python的语法来编写C扩展并可以被Python调用.既具备了Python快速开发的特点,又可以让代码运行起来像C一样快,同时还可以方便地调用C library。

1|11. 环境配置

1|01.1 windows

安装MingW-w64编译器:conda install libpython m2w64-toolchain -c msys2
在Python安装路径下找到\Lib\distutils文件夹,创建distutils.cfg写入如下内容:
[build] compiler=mingw32

1|01.2 macOS

安装XCode

1|01.3 linux

sudo apt-get install build-essential

1|01.4 安装cython

  • pip install cython
  • conda install cython

1|22. 例子:矩阵乘法

1|02.1 python

# dot_python.py import numpy as np def naive_dot(a, b): if a.shape[1] != b.shape[0]: raise ValueError('shape not matched') n, p, m = a.shape[0], a.shape[1], b.shape[1] c = np.zeros((n, m), dtype=np.float32) for i in xrange(n): for j in xrange(m): s = 0 for k in xrange(p): s += a[i, k] * b[k, j] c[i, j] = s return c

1|02.2 cython

# dot_cython.pyx import numpy as np cimport numpy as np cimport cython @cython.boundscheck(False) @cython.wraparound(False) cdef np.ndarray[np.float32_t, ndim=2] _naive_dot(np.ndarray[np.float32_t, ndim=2] a, np.ndarray[np.float32_t, ndim=2] b): cdef np.ndarray[np.float32_t, ndim=2] c cdef int n, p, m cdef np.float32_t s if a.shape[1] != b.shape[0]: raise ValueError('shape not matched') n, p, m = a.shape[0], a.shape[1], b.shape[1] c = np.zeros((n, m), dtype=np.float32) for i in xrange(n): for j in xrange(m): s = 0 for k in xrange(p): s += a[i, k] * b[k, j] c[i, j] = s return c def naive_dot(a, b): return _naive_dot(a, b)

1|02.3 差异点

  • Cython 程序的扩展名是 .pyx

  • cimport 是 Cython 中用来引入 .pxd 文件的命令,可以简单理解成 C/C++ 中用来写声明的头文件

  • @cython.boundscheck(False) 和 @cython.wraparound(False) 两个修饰符用来关闭 Cython 的边界检查

  • Cython 的函数使用 cdef 定义,并且他可以给所有参数以及返回值指定类型。比方说,我们可以这么编写整数 min 函数:

    cdef int my_min(int x, int y): return x if x <= y else y
  • 在函数体内部,我们一样可以使用 cdef typename varname 这样的语法来声明变量

  • 在 Python 程序中,是看不到 cdef 的函数的,所以我们这里 def naive_dot(a, b) 来调用 cdef 过的 _naive_dot 函数

1|02.4 Cython 编译后被 Python 调用

  1. Cython 编译器把 Cython 代码编译成调用了 Python 源码的 C/C++ 代码
  2. 把生成的代码编译成动态链接库
  3. Python 解释器载入动态链接库

前两步

写代码

# setup.py from distutils.core import setup, Extension from Cython.Build import cythonize import numpy setup(ext_modules = cythonize(Extension( 'dot_cython', sources=['dot_cython.pyx'], language='c', include_dirs=[numpy.get_include()], library_dirs=[], libraries=[], extra_compile_args=[], extra_link_args=[] )))
  • 'dot_cython' 是我们要生成的动态链接库的名字
  • sources 里面可以包含 .pyx 文件,以及后面如果我们要调用 C/C++ 程序的话,还可以往里面加 .c / .cpp 文件
  • language 其实默认就是 c,如果要用 C++,就改成 c++ 就好了
  • include_dirs 这个就是传给 gcc 的 -I 参数
  • library_dirs 这个就是传给 gcc 的 -L 参数
  • libraries 这个就是传给 gcc 的 -l 参数
  • extra_compile_args 就是传给 gcc 的额外的编译参数,比方说你可以传一个 -std=c++11
  • extra_link_args 就是传给 gcc 的额外的链接参数(也就是生成动态链接库的时候用的)
  • 如果你从来没见过上面几个 gcc 参数,说明你暂时还没这些需求,等你遇到了你就懂了

然后我们只需要执行下面命令就可以把 Cython 程序编译成动态链接库了。

python setup.py build_ext --inplace

成功运行完上面这句话,可以看到在当前目录多出来了 dot_cython.c 和 dot_cython.so。前者是生成的 C 程序,后者是编译好了的动态链接库。

1|33. 例子:求质数

prime.pyx

# distutils: language=c++ from libcpp.vector cimport vector def prime_py(number): plist = [] for n in range(2, number + 1): for x in range(2, n): if n % x == 0: break else: plist.append(n) return plist def prime_cy(int number): cdef int x, n cdef vector[int] plist for n in range(2, number + 1): for x in range(2, n): if n % x == 0: break else: plist.push_back(n) return plist

setup.py

from setuptools import setup from Cython.Build import cythonize setup( ext_modules=cythonize("prime.pyx") )

准备

python setup.py build_ext --inplace

test_prime.py

import prime import time cy_start = time.time() prime.prime_cy(50000) cy_end = time.time() print('cython : ',cy_end - cy_start) py_start = time.time() prime.prime_py(50000) py_end = time.time() print('python : ',py_end - py_start)

1|44. 例子: 从 c++ 里导入函数

demo.h

#ifndef DEMO_H #define DEMO_H using namespace std; namespace demo { class MyDemo { public: int a; MyDemo(); MyDemo(int a ); ~MyDemo(); int mul(int m ); int add(int b); void sayHello(char* name); }; } int func(int x){ return x * 10; } #endif

demo.cpp

#include "demo.h" #include <iostream> namespace demo { MyDemo::MyDemo () {} MyDemo::MyDemo (int a) { this->a = a; } MyDemo::~MyDemo () {} int MyDemo::mul(int m) { return this->a*m; } int MyDemo::add (int b) { return this->a+b; } void MyDemo::sayHello(char* name){ cout<<"hello "<<name<<"!"<<endl; } }

cdemo.pyd

  • pyd连接c++和pyx
cdef extern from "demo.cpp": pass # Decalre the class with cdef cdef extern from "demo.h" namespace "demo": cdef cppclass MyDemo: MyDemo() except + MyDemo(int) except + int a int mul(int ) int add(int ) void sayHello(char*) cdef extern from "demo.h": int func(int )

cpyep.pyx

  • pyx 连接 pyd 和 python
# distutils: language = c++ from cdemo cimport MyDemo # Create a Cython extension type which holds a C++ instance # as an attribute and create a bunch of forwarding methods # Python extension type. cdef class PyMyDemo: cdef MyDemo c_mydemo # Hold a C++ instance which we're wrapping def __cinit__(self,a): self.c_mydemo = MyDemo(a) def mul(self, m): return self.c_mydemo.mul(m) def add(self,b): return self.c_mydemo.add(b) def sayHello(self,name ): self.c_mydemo.sayHello(name) from cdemo cimport func def funcc(int x): return func(x)

setup.py

  • 生成动态库
  • python setup.py build_ext --inplace
from setuptools import setup from Cython.Build import cythonize setup(ext_modules=cythonize("cpyep.pyx"))

test.py

from cpyep import * print(funcc(10)) # output : 100

1|5一些注意点

  • 函数签名基本上可以原样从 C/C++ 复制到 Cython 中
  • C 中的 _Bool 类型和 C++ 中的 bool 类型在 Cython 中都用 bint 取代(因为 Python 没有布尔类型)
  • struct / enum / union 是支持的
  • const 限定和引用都是支持的
  • 命名空间是支持的
  • C++ 类是支持的
  • 部分操作符重载是支持的,部分操作符需要改名
  • 内嵌类是支持的
  • 模板是支持的
  • 异常是支持的
  • 构造函数、析构函数是支持的
  • 静态成员是支持的
  • libc / libcpp / STL 是支持的
  • 声明写在 .pxd 中可以在 .pyx 中 cimport 进来

__EOF__

本文作者InsiApple
本文链接https://www.cnblogs.com/InsiApple/p/17865276.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   InsiApple  阅读(1579)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示