{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Cython:Cython 语法,调用其他C库" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Cython 语法" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### cdef 关键词" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`cdef` 定义 `C` 类型变量。 \n", "\n", "可以定义局部变量:\n", "\n", "```cython\n", "def fib(int n):\n", " cdef int a,b,i\n", " ...\n", "```\n", "\n", "定义函数返回值:\n", "\n", "```cython\n", "cdef float distance(float *x, float *y, int n):\n", " cdef:\n", " int i\n", " float d = 0.0\n", " for i in range(n):\n", " d += (x[i] - y[i]) ** 2\n", " return d\n", "```\n", "\n", "定义函数:\n", "```cython\n", "cdef class Particle(object):\n", " cdef float psn[3], vel[3]\n", " cdef int id\n", "```\n", "\n", "注意函数的参数不需要使用 cdef 的定义。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### def, cdef, cpdef 函数" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`Cython` 一共有三种定义方式,`def, cdef, cpdef` 三种:\n", "\n", "- `def` - Python, Cython 都可以调用\n", "- `cdef` - 更快,只能 Cython 调用,可以使用指针\n", "- `cpdef` - Python, Cython 都可以调用,不能使用指针" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### cimport" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from math import sin as pysin\n", "from numpy import sin as npsin" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%load_ext Cython" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "从标准 `C` 语言库中调用模块,`cimport` 只能在 `Cython` 中使用:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython\n", "from libc.math cimport sin\n", "from libc.stdlib cimport malloc, free" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### cimport 和 pxd 文件" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如果想在多个文件中复用 `Cython` 代码,可以定义一个 `.pxd` 文件(相当于头文件 `.h`)定义方法,这个文件对应于一个 `.pyx` 文件(相当于源文件 `.c`),然后在其他的文件中使用 `cimport` 导入:\n", "\n", "`fib.pxd, fib.pyx` 文件存在,那么可以这样调用:\n", "```cython\n", "from fib cimport fib\n", "```\n", "\n", "还可以调用 `C++` 标准库和 `Numpy C Api` 中的文件:\n", "```cython\n", "from libcpp.vector cimport vector\n", "cimport numpy as cnp\n", "```" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## 调用其他C库" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "从标准库 `string.h` 中调用 `strlen`:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing len_extern.pyx\n" ] } ], "source": [ "%%file len_extern.pyx\n", "cdef extern from \"string.h\":\n", " int strlen(char *c)\n", " \n", "def get_len(char *message):\n", " return strlen(message)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "不过 `Cython` 不会自动扫描导入的头文件,所以要使用的函数必须再声明一遍:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing setup_len_extern.py\n" ] } ], "source": [ "%%file setup_len_extern.py\n", "from distutils.core import setup\n", "from distutils.extension import Extension\n", "from Cython.Distutils import build_ext\n", "\n", "setup(\n", " ext_modules=[ Extension(\"len_extern\", [\"len_extern.pyx\"]) ],\n", " cmdclass = {'build_ext': build_ext}\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "编译:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "running build_ext\n", "cythoning len_extern.pyx to len_extern.c\n", "building 'len_extern' extension\n", "creating build\n", "creating build\\temp.win-amd64-2.7\n", "creating build\\temp.win-amd64-2.7\\Release\n", "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\\Miniconda\\include -IC:\\Miniconda\\PC -c len_extern.c -o build\\temp.win-amd64-2.7\\Release\\len_extern.o\n", "writing build\\temp.win-amd64-2.7\\Release\\len_extern.def\n", "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -shared -s build\\temp.win-amd64-2.7\\Release\\len_extern.o build\\temp.win-amd64-2.7\\Release\\len_extern.def -LC:\\Miniconda\\libs -LC:\\Miniconda\\PCbuild\\amd64 -lpython27 -lmsvcr90 -o \"C:\\Users\\Jin\\Documents\\Git\\python-tutorial\\07. interfacing with other languages\\len_extern.pyd\"\n" ] } ], "source": [ "!python setup_len_extern.py build_ext --inplace" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "从 `Python` 中调用:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [], "source": [ "import len_extern" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "调用这个模块后,并不能直接使用 `strlen` 函数,可以看到,这个模块中并没有 `strlen` 这个函数:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['__builtins__',\n", " '__doc__',\n", " '__file__',\n", " '__name__',\n", " '__package__',\n", " '__test__',\n", " 'get_len']" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dir(len_extern)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "不过可以调用 `get_len` 函数: " ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len_extern.get_len('hello')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "因为调用的是 `C` 函数,所以函数的表现与 `C` 语言的用法一致,例如 `C` 语言以 `\\0` 为字符串的结束符,所以会出现这样的情况:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len_extern.get_len('hello\\0world!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "除了对已有的 `C` 函数进行调用,还可以对已有的 `C` 结构体进行调用和修改:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing time_extern.pyx\n" ] } ], "source": [ "%%file time_extern.pyx\n", "cdef extern from \"time.h\":\n", "\n", " struct tm:\n", " int tm_mday\n", " int tm_mon\n", " int tm_year\n", "\n", " ctypedef long time_t\n", " tm* localtime(time_t *timer)\n", " time_t time(time_t *tloc)\n", "\n", "def get_date():\n", " \"\"\"Return a tuple with the current day, month and year.\"\"\"\n", " cdef time_t t\n", " cdef tm* ts\n", " t = time(NULL)\n", " ts = localtime(&t)\n", " return ts.tm_mday, ts.tm_mon + 1, ts.tm_year + 1900" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这里我们只使用 `tm` 结构体的年月日信息,所以只声明了要用了三个属性。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing setup_time_extern.py\n" ] } ], "source": [ "%%file setup_time_extern.py\n", "from distutils.core import setup\n", "from distutils.extension import Extension\n", "from Cython.Distutils import build_ext\n", "\n", "setup(\n", " ext_modules=[ Extension(\"time_extern\", [\"time_extern.pyx\"]) ],\n", " cmdclass = {'build_ext': build_ext}\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "编译:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "running build_ext\n", "cythoning time_extern.pyx to time_extern.c\n", "building 'time_extern' extension\n", "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\\Miniconda\\include -IC:\\Miniconda\\PC -c time_extern.c -o build\\temp.win-amd64-2.7\\Release\\time_extern.o\n", "writing build\\temp.win-amd64-2.7\\Release\\time_extern.def\n", "C:\\Miniconda\\Scripts\\gcc.bat -DMS_WIN64 -shared -s build\\temp.win-amd64-2.7\\Release\\time_extern.o build\\temp.win-amd64-2.7\\Release\\time_extern.def -LC:\\Miniconda\\libs -LC:\\Miniconda\\PCbuild\\amd64 -lpython27 -lmsvcr90 -o \"C:\\Users\\Jin\\Documents\\Git\\python-tutorial\\07. interfacing with other languages\\time_extern.pyd\"\n" ] } ], "source": [ "!python setup_time_extern.py build_ext --inplace" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "测试:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "(19, 9, 2015)" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import time_extern\n", "\n", "time_extern.get_date()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "清理文件:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import zipfile\n", "\n", "f = zipfile.ZipFile('07-04-extern.zip','w',zipfile.ZIP_DEFLATED)\n", "\n", "names = ['setup_len_extern.py',\n", " 'len_extern.pyx',\n", " 'setup_time_extern.py',\n", " 'time_extern.pyx']\n", "for name in names:\n", " f.write(name)\n", "\n", "f.close()\n", "\n", "!rm -f setup*.*\n", "!rm -f len_extern.*\n", "!rm -f time_extern.*\n", "!rm -rf build" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.9" } }, "nbformat": 4, "nbformat_minor": 0 }