python高性能扩展工具-cython教程2基础

本章涉及的主要内容:

  • 链接模型
  • Cython关键字 - cdef
  • Typedef和函数指针
  • public关键字
  • 关键字cpdef
  • C/C+++调用Python logging
  • C/C++用Python ConfigParser
  • Python到C/C++的回调
  • Cython PXD
  • 与构建系统集成

链接模型

C/C++中嵌入python

image.png

cython模式

image.png

cdef

cdef关键字告诉编译器该语句是本地C类型或本地函数。比如:

cdef int AddFunction(int, int)

def square(int x):
    return x ** 2

Struct

C的struct 可以直接在Cython中使用。比如mycode.h


#ifndef __MYCODE_H__
#define __MYCODE_H__

struct mystruct {
  char * string;
  int integer;
  char ** string_array;
};

extern void printStruct (struct mystruct *);

#endif //__MYCODE_H__

printStruct函数的实现 mycode.c

#include <stdio.h>
#include "mycode.h"

void printStruct (struct mystruct * s)
{
  printf (".string = %s\n", s->string);
  printf (".integer = %i\n", s->integer);
  printf (".string_array = \n");

  int i;
  for (i = 0; i < s->integer; ++i)
    printf ("\t[%i] = %s\n", i, s->string_array [i]);
}

Python导入调用:mycodepy.pyx

cdef extern from "mycode.h":
  struct mystruct:
    char * string
    int integer
    char ** string_array
  void printStruct (mystruct *)

def testStruct ():
    cdef mystruct s
    cdef char *array [2]
    s.string = "Hello World"
    s.integer = 2
    array [0] = "foo"
    array [1] = "bar"
    s.string_array = array
    printStruct (&s)

嵌套结构体void myfunc (struct mystruct * x)在cython的定义:void myfunc (mystruct * x)。cython中没有→操作符号,要使用点号。

执行

$ make
cython -3 -o mycodepy.c mycodepy.pyx
gcc -g -O2 -fpic -c mycodepy.c -o mycodepy.o `python3-config --cflags`
gcc -g -O2 -fpic -c mycode.c -o mycode.o
gcc -g -O2 -shared -o mycodepy.so mycode.o mycodepy.o
$ python
Python 3.6.5 |Anaconda, Inc.| (default, Apr 29 2018, 16:14:56) 
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from mycodepy import testStruct
>>> from mycodepy import testStruct
>>> testStruct ()
.string = Hello World
.integer = 2
.string_array = 
    [0] = foo
    [1] = bar

Enum

c中的实现:


enum cardsuit {
    CLUBS,
    DIAMONDS,
    HEARTS,
    SPADES
};

Cython定义


cdef enum cardsuit:
    CLUBS, DIAMONDS, HEARTS, SPADES

# 使用
cdef cardsuit card = CLUBS

Typedef和函数指针

c中的实现:


struct foobar {
    int x;
    char * y;
};

typedef struct foobar foobar_t;
typedef void (*cfptr) (int)

Cython定义


cdef struct foobar:
    int x
    char * y
ctypedef foobar foobar_t

# 使用
ctypedef int * int_ptr
ctypedef void (*cfptr)(int
cdef cfptr myfunctionptr = &myfunc

Typedef和函数指针

这是Cython中非常强大的关键字。 它允许任何带有public修饰符的cdef声明输出相应的C/C++头,其中相关声明可从C/C++访问。 例如,我们可以声明:


cdef public struct CythonStruct:
    size_t number_of_elements;
    char ** elements;

编译后会生成cython_input.h


struct CythonStruct {
    size_t number_of_elements;
    char ** elements;
};

c调用要求在嵌入libpython.so,并执行初始化:

#include <Python.h>

int main(int argc, char **argv) {
    Py_Initialize ();
    // code in here
    Py_Finalize ();
    return 0;
}

调用前需要初始化cythonfile.pyx(如果有)的

cdef public void cythonFunction ():
    print "inside cython function!!!"

这样会生成cythonfile.c和cythonfile.h。

/* Boiler plate init Python */
Py_SetProgramName (argv [0]);
Py_Initialize ();
/* Init our config module into Python memory */
initpublicTest ();
cythonFunction ();
/* cleanup python before exit... */
Py_Finalize ();

这个步骤类似python中的import cythonfile

cpdef

函数声明def在python和cython中可调用,cdef在c系列可以调用,cpdef在两者都可以调用,但是返回要知道类型,且丧失了cython的类型安全,不推荐这么做。

long returnValue = PyInt_AsLong (test (1, 0))

实例:C/C++调用python logging

main.c

#include "NativeLogging.h"

int main(int argc, char **argv)
{
    // we want to ensure we use a command line argument for the output log file
    if (argc < 2) {
        return -1;
    }

    // use the first argument as log file
    SetupNativeLogging(argv[1]);

    // log out some stuff at different levels
    info("info message");
    debug("debug message");
    error("error message");

    // close up everything including Python
    CloseNativeLogging();

    return 0;
}

NativeLogging.h

#ifndef __NATIVE_LOGGING_H__
#define __NATIVE_LOGGING_H__

#define printflike __attribute__ ((format (printf, 3, 4)))

extern void printflike native_logging_info(const char *, unsigned, const char *, ...);
extern void printflike native_logging_debug(const char *, unsigned, const char *, ...);
extern void printflike native_logging_error(const char *, unsigned, const char *, ...);

#define info(...)  native_logging_info(__FILE__, __LINE__, __VA_ARGS__)
#define error(...) native_logging_debug(__FILE__, __LINE__, __VA_ARGS__)
#define debug(...) native_logging_error(__FILE__, __LINE__, __VA_ARGS__)

extern void SetupNativeLogging(const char * logFileName);
extern void CloseNativeLogging();

#endif // __NATIVE_LOGGING_H__

NativeLogging.c

#include <Python.h>
#include <stdio.h>
#include <stdarg.h>

#include "PythonLoggingBackend.h"
#include "NativeLogging.h"

void native_logging_info(const char * file, unsigned line, const char * fmt, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, fmt);
    vsprintf(buffer, fmt, args);
    va_end(args);

    char buf[512];
    snprintf(buf, sizeof(buf), "%s:%i -> %s", file, line, buffer);
    python_info(buf);
}

void native_logging_debug(const char * file, unsigned line,const char * fmt, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, fmt);
    vsprintf(buffer, fmt, args);
    va_end(args);

    char buf[512];
    snprintf(buf, sizeof(buf), "%s-%i -> %s", file, line, buffer);
    python_debug(buf);
}

void native_logging_error(const char * file, unsigned line, const char * fmt, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, fmt);
    vsprintf(buffer, fmt, args);
    va_end(args);

    char buf[512];
    snprintf(buf, sizeof(buf), "%s:%i -> %s", file, line, buffer);
    python_error(buf);
}

void SetupNativeLogging(const char * logFileName)
{
    /* Boiler plate init Python */
    Py_Initialize();

    /* Init our config module into Python memory */
    initPythonLoggingBackend();

    /* call directly into our cython module parseConfig */
    initLoggingWithLogFile(logFileName);
}

void CloseNativeLogging()
{
    /* cleanup python before exit ... */
    Py_Finalize();
}

PythonLoggingBackend.pyx

import logging

cdef public void initLoggingWithLogFile(const char * logfile):
    logging.basicConfig(filename = logfile,
                        level = logging.DEBUG,
                        format = '%(levelname)s %(asctime)s: %(message)s',
                        datefmt = '%m/%d/%Y %I:%M:%S')

cdef public void python_info(char * message):
    logging.info(message)

cdef public void python_debug(char * message):
    logging.debug(message)

cdef public void python_error(char * message):
    logging.error(message)

Makefile

all:
    cython -2 PythonLoggingBackend.pyx
    gcc -g -O2 -fpic -c PythonLoggingBackend.c -o PythonLoggingBackend.o `python-config --includes`
    gcc -g -O2 -fpic -c NativeLogging.c -o NativeLogging.o  `python-config --includes`
    gcc -g -O2 -fpic -c main.c -o main.o
    gcc -g -O2 -o example main.o PythonLoggingBackend.o NativeLogging.o `python-config --libs`

clean:
    rm -f example PythonLoggingBackend.c *.o PythonLoggingBackend.h *.log

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容