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)
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