程序员软件测试Python专家之路工具癖

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

2018-09-21  本文已影响54人  python测试开发

本章涉及的主要内容:

链接模型

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

上一篇 下一篇

猜你喜欢

热点阅读