PY08-05:Python的C扩展模块


  Python在提供傻白甜这样的编程方式的时候,对性能就是硬伤了,所以性能这块提供了C的扩展模块,实际上Python是与本地二进制可执行动态库是无缝调用的。上一个主题解释了Python调用动态库,但是那一种方式需要太多C的知识,其实Python还提供了一种无缝自动的调用方式,在Python成为built-in实现。这个主题就介绍Python的C扩展。顺便还介绍了怎么制作Python安装包


Python的C扩展的模式

  • 提示:只争对Python3的情况说明,对Python2放弃了。

Python与C扩展动态库之间的工作关系

Python与C扩展动态库的工作关系
  • Python调用C动态库的过程
    1. 编写Python代码test.pyimport 模块名直接加载模块;
    2. Python虚拟机执行test.py代码,会根据import的模块名查找模块,模块可以是是python模块或者dll模块,dll模块查找规则是:模块名.其他任意描述.pyd
    3. 查找到以后,执行PyInit_模块名初始化函数完成dll的初始化;
    4. PyInit_模块名初始化函数完成python中函数名与扩展接口包装函数名的映射;
    5. 当调用模块中函数的时候,python会根据python函数名找到C扩展包装函数名;
    6. C扩展包装函数调用任意C实现的代码。

C动态库的编译

  • C的动态库使用python的C库与头文件

    • 实际没有额外dll的库,所有代码都封装在h文件中。
      • Python.h
      • structmember.h
  • 不同的平台或者编译器编译的指令都有差异,但是编译的结构都是动态库

    1. gcc是编译so文件;(mac的clang编译器可能是dylib)
    2. link是编译dll文件;
  • 说明:

    • 尽管是动态库,但是Python扩展的工作规范是pyd扩展名,不使用so、dll、dylib文件名扩展名。

C扩展接口的编程模式

包装函数实现

  • 包装函数可以自己实现,也可以调用C的其他实现,包含C动态库。下面是一个代码例子,重点是:

    1. 包装器函数形式三种:
      • 一般参数:typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
      • 命名参数:typedef PyObject *(*PyCFunctionWithKeywords)(PyObject *, PyObject *, PyObject *);
      • 无参数:typedef PyObject *(*PyNoArgsFunction)(PyObject *);
    2. 参数的转换处理(把PyObject*类型转换为C类型):
      • 解析命名参数:PyArg_ParseTupleAndKeywords
      • 解析一般参数:PyArg_ParseTuple
    3. 返回值得转换处理:(如果已经是PyObject*类型则不需要转换)
      • 调用Py_BuildValue函数把C类型转换为PyObject*类型
  • 包装器函数可以随意命名,但是建议采用模块名_函数名,我们使用的是py_函数名


static char py_gcd_doc[] = "最大公约数";
static PyObject* py_gcd(PyObject*self, PyObject *args){
    int r, x, y;
    // 解析参数
    if(!PyArg_ParseTuple(args, "ii:gcd", &x, &y)){
        return NULL;
    }
    r = gcd(x, y);
    return Py_BuildValue("i", r); 
}
static char py_replace_doc[] = "字符替换";
static PyObject* py_replace(PyObject* self, PyObject *args, PyObject *kwargs){
    char *s, *sdup;
    char och, nch;
    int nrep;

    PyObject *result;
    static char *argsname[] = {"s", "och", "nch", NULL};
    // 解析参数
    if(!PyArg_ParseTupleAndKeywords(args, kwargs, "scc:replace", argsname, &s, &och, &nch)){
        return NULL;
    }
    sdup = (char *)malloc(strlen(s) + 1);
    strcpy(sdup, s);
    nrep = replace(sdup, och, nch);
    result = Py_BuildValue("(is)", nrep, sdup);
    free(sdup);
    return result;
}
static char py_distance_doc[] = "计算距离";
static PyObject* py_distance(PyObject *self, PyObject *args){
    PyErr_SetString(PyExc_NotImplementedError, "distance() not implements");
    return NULL;
}

附录:上面调用的三个函数的C实现

头文件:C.h

#ifndef C_YQ_H
#define C_YQ_H
#include <stdio.h>
#include <string.h>
#include <math.h>

typedef struct Point{
    double x;
    double y;
} Point;


extern int gcd(int x, int y);
extern int replace(char *s, char och, char nch);
extern double distance(Point *a, Point *b);

#define MAGIC  0x31337

#endif

实现文件:C.c

#include "C.h"
int gcd(int x, int y){
    /* 
        这个算法是来自最大公约数的一个性质
        gcd(a,b)=gcd(b, a mod b)
        gcd(a,b)=gcd(b, a-b)
    */
    int g;
    g = y;
    while(x > 0){
        g = x;
        x = y % x;
        y = g;
    }
    return g;
}


int replace(char *s, char och, char nch){
    int nrep = 0;
    while(s = strchr(s, och)){
        *(s++) = nch;
        nrep++;
    }
    return nrep;
}

double distance(Point *a, Point *b){
    double dx, dy;
    dx = a->x - b->x;
    dy = a->y - b->y;
    return sqrt(dx * dx + dy * dy);
}
// 测试代码调用的,编译成执行文件执行:cl /EHsc /MD /utf-8 /nologo C.c /link /MACHINE:X64 /NOLOGO /OUT:main.exe  
// int main(){
//     printf("公约数是:%d\n", gcd(100,254));
//     char *str = "Hello this is a world!";
//     printf("替换次数:%d\n", replace(str, 'i','X'));
//     printf("替换结果:%s\n", str);
//     Point a = {0, 1};
//     Point b = {1, 0};
//     printf("距离:%f\n", distance(&a, &b));
//     return 0;
// }

定义模块

PyModuleDef结构体

  • 模块定义在Python的C扩展中是一个结构体:PyModuleDef, 创建模块的使用,就是使用这个定义模块,这个结构体的定义如下:
typedef struct PyModuleDef{
  PyModuleDef_Base m_base;
  const char* m_name;
  const char* m_doc;
  Py_ssize_t m_size;
  PyMethodDef *m_methods;
  struct PyModuleDef_Slot* m_slots;
  traverseproc m_traverse;
  inquiry m_clear;
  freefunc m_free;
} PyModuleDef;
  • 其中的核心是前面5个成员的定义是必须。
    • PyModuleDef_Base m_base;
      • 这是指定基,使用一个宏指定:PyModuleDef_HEAD_INIT
    • const char* m_name;
      • 模块名:这个名字也是编译后的dll/so的名字。
    • const char* m_doc;
      • 文档,可以指定为NULL。
    • Py_ssize_t m_size;
      • 使用-1标识自动指定大小。
    • PyMethodDef *m_methods;
      • 模块中的方法/函数定义。这里指针是指的边长动态数组。

PyMethodDef结构体

  • 模块中导出的方法/函数定义, 使用结构体定义:PyMethodDef
struct PyMethodDef {
    const char  *ml_name;   /* The name of the built-in function/method */
    PyCFunction ml_meth;    /* The C function that implements it */
    int         ml_flags;   /* Combination of METH_xxx flags, which mostly
                               describe the args expected by the C func */
    const char  *ml_doc;    /* The __doc__ attribute, or NULL */
};
typedef struct PyMethodDef PyMethodDef;
  • PyMethodDef结构体成员说明:
    • const char *ml_name
      • python中调用的函数名:
    • PyCFunction ml_meth;
      • C扩展包装函数名,其真正类型是PyCFunction + PyCFunctionWithKeywords + PyNoArgsFunction,为了防止编译警告,需要类型转换为PyCFunction
    • int ml_flags:
      • 指定参数类型:PyCFunction + PyCFunctionWithKeywords + PyNoArgsFunction,使用对应的宏
        • #define METH_VARARGS 0x0001
        • #define METH_KEYWORDS 0x0002
        • #define METH_NOARGS 0x0004
      • METH_VARARGS必须与METH_KEYWORDS联合使用。
    • const char *ml_doc
      • 函数的文档定义。

模块定义的例子

  • 先定义:PyMethodDef,再定义:PyModuleDef
static PyMethodDef _mymethos[] = {
    {"gcd",      py_gcd,                   METH_VARARGS,                 py_gcd_doc},
    {"replace",  (PyCFunction)py_replace,  METH_KEYWORDS | METH_VARARGS, py_replace_doc},    // 因为带三个参数,属于PyCFunctionWithKeywords
    {"distance", py_distance,              METH_VARARGS,                 py_distance_doc},
    {NULL,       NULL,                     0,                            NULL}
};


static struct PyModuleDef _mymodule = {
    PyModuleDef_HEAD_INIT,
    "mymodule",
    NULL,
    -1,
    _mymethos
};

创建模块

  • 创建模块必须在导出函数中创建:

导出函数

  • 导出函数规范:

    • PyMODINIT_FUNC PyInit_模块名(void)
      • 返回值:PyMODINIT_FUNC,本质是PyObject指针类型,也就是是一个模块对象,使用:模块名.函数可以在Python中调用。
        • define PyMODINIT_FUNC __declspec(dllexport) PyObject*
      • 参数:无
      • 函数名:PyInit_作为前缀,后面加上模块名。模块名以PyModuleDef对象中定义的名字为准。
  • 导出函数的例子:


PyMODINIT_FUNC PyInit_mymodule(void){
    return PyMODINIT_FUNC对象;
}

创建模块并返回

  • 模块创建使用函数PyModule_Create创建,函数原型是:

    • PyAPI_FUNC(PyObject *) PyModule_Create2(struct PyModuleDef*, int apiver);
      • 第2个参数使用api把把版本,是固定的值:#define PYTHON_API_VERSION 1013
      • 第一个参数类型是:PyModuleDef *
      • 返回值:PyObject指针。
  • 模块撞见的例子:


PyMODINIT_FUNC PyInit_mymodule(void){
    PyObject *mod;
    mod = PyModule_Create(&_mymodule);
    return mod;
}

Python的C扩展例子

  • 提示:使用《Python参考手册(第4版)》中第26章的例子。

C功能实现

C头文件:C.h

#ifndef C_YQ_H
#define C_YQ_H
#include <stdio.h>
#include <string.h>
#include <math.h>

typedef struct Point{
    double x;
    double y;
} Point;


extern int gcd(int x, int y);
extern int replace(char *s, char och, char nch);
extern double distance(Point *a, Point *b);

#define MAGIC  0x31337

#endif

C实现文件:C.c

#include "C.h"
int gcd(int x, int y){
    /* 
        这个算法是来自最大公约数的一个性质
        gcd(a,b)=gcd(b, a mod b)
        gcd(a,b)=gcd(b, a-b)
    */
    int g;
    g = y;
    while(x > 0){
        g = x;
        x = y % x;
        y = g;
    }
    return g;
}


int replace(char *s, char och, char nch){
    int nrep = 0;
    while(s = strchr(s, och)){
        *(s++) = nch;
        nrep++;
    }
    return nrep;
}

double distance(Point *a, Point *b){
    double dx, dy;
    dx = a->x - b->x;
    dy = a->y - b->y;
    return sqrt(dx * dx + dy * dy);
}
// 测试代码调用的,编译成执行文件执行:cl /EHsc /MD /utf-8 /nologo C.c /link /MACHINE:X64 /NOLOGO /OUT:main.exe  
// int main(){
//     printf("公约数是:%d\n", gcd(100,254));
//     char *str = "Hello this is a world!";
//     printf("替换次数:%d\n", replace(str, 'i','X'));
//     printf("替换结果:%s\n", str);
//     Point a = {0, 1};
//     Point b = {1, 0};
//     printf("距离:%f\n", distance(&a, &b));
//     return 0;
// }

C扩展实现

#include <Python.h>
#include "C.h"


static char py_gcd_doc[] = "最大公约数";
static PyObject* py_gcd(PyObject*self, PyObject *args){
    int r, x, y;
    // 解析参数
    if(!PyArg_ParseTuple(args, "ii:gcd", &x, &y)){
        return NULL;
    }
    r = gcd(x, y);
    return Py_BuildValue("i", r); 
}

static char py_replace_doc[] = "字符替换";
static PyObject* py_replace(PyObject* self, PyObject *args, PyObject *kwargs){
    char *s, *sdup;
    char och, nch;
    int nrep;

    PyObject *result;
    static char *argsname[] = {"s", "och", "nch", NULL};
    // 解析参数
    if(!PyArg_ParseTupleAndKeywords(args, kwargs, "scc:replace", argsname, &s, &och, &nch)){
        return NULL;
    }
    sdup = (char *)malloc(strlen(s) + 1);
    strcpy(sdup, s);
    nrep = replace(sdup, och, nch);
    result = Py_BuildValue("(is)", nrep, sdup);
    free(sdup);
    return result;
}

static char py_distance_doc[] = "计算距离";
static PyObject* py_distance(PyObject *self, PyObject *args){
    PyErr_SetString(PyExc_NotImplementedError, "distance() not implements");
    return NULL;
}

static PyMethodDef _mymethos[] = {
    {"gcd",      py_gcd,                   METH_VARARGS,                 py_gcd_doc},
    {"replace",  (PyCFunction)py_replace,  METH_KEYWORDS | METH_VARARGS, py_replace_doc},    // 因为带三个参数,属于PyCFunctionWithKeywords
    {"distance", py_distance,              METH_VARARGS,                 py_distance_doc},
    {NULL,       NULL,                     0,                            NULL}
};


static struct PyModuleDef _mymodule = {
    PyModuleDef_HEAD_INIT,
    "mymodule",
    NULL,
    -1,
    _mymethos
};

PyMODINIT_FUNC PyInit_mymodule(void){
    PyObject *mod;
    mod = PyModule_Create(&_mymodule);
    PyModule_AddIntMacro(mod, MAGIC);
    return mod;
}

编译脚本

  1. 脚本:Makefile
pyd: C.c C.h C_ython.c
# 切换utf-8编码
    @chcp 65001
# 编译C.c
    @cl /c /EHsc /MD /utf-8 /nologo /Fo:C.obj  C.c
# 编译C_ython.c
    @cl /c /EHsc /MD /utf-8 /nologo "-IC:\Program Files\Python36\include" /Fo:C_ython.obj  C_ython.c
# 链接动态库
    @link /MACHINE:X64 /NOLOGO /DLL  /OUT:mymodule.pyd  /EXPORT:PyInit_mymodule C.obj C_ython.obj
clean:
    @del *.obj *.dll *.pdb *.ilk *.exe *.lib  *.exp  *.pyd 2>/Nula
  1. 执行脚本:
    • vcvars64.bat
    • nmake pyd
  • 注意:
    • vcvars64.bat安装Visual Studio后就有的一个C++编译环境配置脚本。
  1. 编译效果
编译与结果截图

Python调用代码

  • Python调用就像Python语法一样
# 模块导入
import mymodule

# 模块调用
print(mymodule.gcd(6, 8))

Python执行效果

  • 执行python:python test.py
    • 在Linux下,直接使用脚本执行更加方便。
Python调用C扩展模块的结果

观察下pyd的导出信息

Python的C扩展模块的导出信息
  • 从此C的动态库可以作为Python模块的扩展形式存在,与Python模块一样的使用。
    • 这也是所谓built-in内置函数与类的实现机制与来源。不见代码,只见模块。真是酸爽啊!

C扩展的核心

  • C扩展都是有编程模式的,模式清楚后,剩余的就是参数与返回值的类型处理,这是最大的麻烦。后面单独开一个主题说明。

Python的扩展编译

  • 上面的内容完全是利用C的编程技术,包含Python的C扩展接口技术,实际Python提供了更多自动化的工作机制

扩展编译

  • 扩展编译就是不需要自己编写Makefile这样的编译脚本,而是Python自动提供编译配置。

setup.py文件模式

  • setup.py文件一般编程模式
from distutils.core import setup, Extension
setup(
    name="my",
    version="1.1",
    ext_modules=[
        Extension("mymodule", sources=["C_ython.c", "C.c"],language="C"), 
    ]
)

  • 那么是包名

setup函数

  • setup函数是一个setup脚本需要的工作设置。
from distutils.core import setup, Extension
help(setup)
Help on function setup in module distutils.core:

setup(**attrs)
    The gateway to the Distutils: do everything your setup script needs
    to do, in a highly flexible and user-driven way.  Briefly: create a
    Distribution instance; find and parse config files; parse the command
    line; run each Distutils command found there, customized by the options
    supplied to 'setup()' (as keyword arguments), in config files, and on
    the command line.
    
    The Distribution instance might be an instance of a class supplied via
    the 'distclass' keyword argument to 'setup'; if no such class is
    supplied, then the Distribution class (in dist.py) is instantiated.
    All other arguments to 'setup' (except for 'cmdclass') are used to set
    attributes of the Distribution instance.
    
    The 'cmdclass' argument, if supplied, is a dictionary mapping command
    names to command classes.  Each command encountered on the command line
    will be turned into a command class, which is in turn instantiated; any
    class found in 'cmdclass' is used in place of the default, which is
    (for command 'foo_bar') class 'foo_bar' in module
    'distutils.command.foo_bar'.  The command class must provide a
    'user_options' attribute which is a list of option specifiers for
    'distutils.fancy_getopt'.  Any command-line options between the current
    and the next command are used to set attributes of the current command
    object.
    
    When the entire command-line has been successfully parsed, calls the
    'run()' method on each command object in turn.  This method will be
    driven entirely by the Distribution object (which each command object
    has a reference to, thanks to its constructor), and the
    command-specific options that became attributes of each command
    object.

setup.py的常见任务

C:\01works\13python\codes\cython_py>python setup.py --help-commands
Standard commands:
  build            build everything needed to install
  build_py         "build" pure Python modules (copy to build directory)
  build_ext        build C/C++ extensions (compile/link to build directory)
  build_clib       build C/C++ libraries used by Python extensions
  build_scripts    "build" scripts (copy and fixup #! line)
  clean            clean up temporary files from 'build' command
  install          install everything from build directory
  install_lib      install all Python modules (extensions and pure Python)
  install_headers  install C/C++ header files
  install_scripts  install scripts (Python or otherwise)
  install_data     install data files
  sdist            create a source distribution (tarball, zip file, etc.)
  register         register the distribution with the Python package index
  bdist            create a built (binary) distribution
  bdist_dumb       create a "dumb" built distribution
  bdist_rpm        create an RPM distribution
  bdist_wininst    create an executable installer for MS Windows
  check            perform some checks on the package
  upload           upload binary package to PyPI

usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commands
   or: setup.py cmd --help
  • 我们主要使用build_ext

build_ext的选项

C:\01works\13python\codes\cython_py>python setup.py build_ext --help
Common commands: (see '--help-commands' for more)

  setup.py build      will build the package underneath 'build/'
  setup.py install    will install the package

Global options:
  --verbose (-v)  run verbosely (default)
  --quiet (-q)    run quietly (turns verbosity off)
  --dry-run (-n)  don't actually do anything
  --help (-h)     show detailed help message
  --no-user-cfg   ignore pydistutils.cfg in your home directory

Options for 'build_ext' command:
  --build-lib (-b)     directory for compiled extension modules
  --build-temp (-t)    directory for temporary files (build by-products)
  --plat-name (-p)     platform name to cross-compile for, if supported
                       (default: win-amd64)
  --inplace (-i)       ignore build-lib and put compiled extensions into the
                       source directory alongside your pure Python modules
  --include-dirs (-I)  list of directories to search for header files
                       (separated by ';')
  --define (-D)        C preprocessor macros to define
  --undef (-U)         C preprocessor macros to undefine
  --libraries (-l)     external C libraries to link with
  --library-dirs (-L)  directories to search for external C libraries
                       (separated by ';')
  --rpath (-R)         directories to search for shared C libraries at runtime
  --link-objects (-O)  extra explicit link objects to include in the link
  --debug (-g)         compile/link with debugging information
  --force (-f)         forcibly build everything (ignore file timestamps)
  --compiler (-c)      specify the compiler type
  --parallel (-j)      number of parallel build jobs
  --swig-cpp           make SWIG create C++ files (default is C)
  --swig-opts          list of SWIG command line options
  --swig               path to the SWIG executable
  --user               add user include, library and rpath
  --help-compiler      list available compilers

usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commands
   or: setup.py cmd --help

Extension类

from distutils.core import setup, Extension
help(Extension)
Help on class Extension in module distutils.extension:

class Extension(builtins.object)
 |  Just a collection of attributes that describes an extension
 |  module and everything needed to build it (hopefully in a portable
 |  way, but there are hooks that let you be as unportable as you need).
 |  
 |  Instance attributes:
 |    name : string
 |      the full name of the extension, including any packages -- ie.
 |      *not* a filename or pathname, but Python dotted name
 |    sources : [string]
 |      list of source filenames, relative to the distribution root
 |      (where the setup script lives), in Unix form (slash-separated)
 |      for portability.  Source files may be C, C++, SWIG (.i),
 |      platform-specific resource files, or whatever else is recognized
 |      by the "build_ext" command as source for a Python extension.
 |    include_dirs : [string]
 |      list of directories to search for C/C++ header files (in Unix
 |      form for portability)
 |    define_macros : [(name : string, value : string|None)]
 |      list of macros to define; each macro is defined using a 2-tuple,
 |      where 'value' is either the string to define it to or None to
 |      define it without a particular value (equivalent of "#define
 |      FOO" in source or -DFOO on Unix C compiler command line)
 |    undef_macros : [string]
 |      list of macros to undefine explicitly
 |    library_dirs : [string]
 |      list of directories to search for C/C++ libraries at link time
 |    libraries : [string]
 |      list of library names (not filenames or paths) to link against
 |    runtime_library_dirs : [string]
 |      list of directories to search for C/C++ libraries at run time
 |      (for shared extensions, this is when the extension is loaded)
 |    extra_objects : [string]
 |      list of extra files to link with (eg. object files not implied
 |      by 'sources', static library that must be explicitly specified,
 |      binary resource files, etc.)
 |    extra_compile_args : [string]
 |      any extra platform- and compiler-specific information to use
 |      when compiling the source files in 'sources'.  For platforms and
 |      compilers where "command line" makes sense, this is typically a
 |      list of command-line arguments, but for other platforms it could
 |      be anything.
 |    extra_link_args : [string]
 |      any extra platform- and compiler-specific information to use
 |      when linking object files together to create the extension (or
 |      to create a new static Python interpreter).  Similar
 |      interpretation as for 'extra_compile_args'.
 |    export_symbols : [string]
 |      list of symbols to be exported from a shared extension.  Not
 |      used on all platforms, and not generally necessary for Python
 |      extensions, which typically export exactly one symbol: "init" +
 |      extension_name.
 |    swig_opts : [string]
 |      any extra options to pass to SWIG if a source file has the .i
 |      extension.
 |    depends : [string]
 |      list of files that the extension depends on
 |    language : string
 |      extension language (i.e. "c", "c++", "objc"). Will be detected
 |      from the source extensions if not provided.
 |    optional : boolean
 |      specifies that a build failure in the extension should not abort the
 |      build process, but simply not install the failing extension.
 |  
 |  Methods defined here:
 |  
 |  __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, library_dirs=None, libraries=None, runtime_library_dirs=None, extra_objects=None, extra_compile_args=None, extra_link_args=None, export_symbols=None, swig_opts=None, depends=None, language=None, optional=None, **kw)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __repr__(self)
 |      Return repr(self).
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)

扩展编译结果

  • 执行命令:
    • python setup.py build_ext --inplace
使用python的扩展编译实现编译
  • 这个编译不需要执行vcvars64,因为所有的选项都这是在命令行了。

  • 然后执行test.py测试下效果,这个与直接用C编译器编译的调用一样。

  • 更多的setup.py使用,请参考:

    • https://thomasnyberg.com/cpp_extension_modules.html
    • https://packaging.python.org/guides/packaging-binary-extensions/
    • https://packaging.python.org/tutorials/packaging-projects/

包装扩展模块的Python模块

  • 一般不直接调用Python的C扩展模块,一般会使用Python包装一下。
  1. 包装模块
    • 文件名:my.py
from mymodule import *

  1. 调用模块
    • test.py
import my
print(my.gcd(6, 8))

安装

安装的目录结构

  • 一定要清楚自己需要安装什么,目录结构需要清楚。

  • 安装包含两种类型的文件:

    1. Python的代码
    2. Python的扩展模块(直接执行Python模块也可以)
  • 下面是需要安装的目录包

    • 需要编译安装的Python的C扩展模块源代码
      • C.h
      • C.c
      • C_ython.c
    • python的封装代码(包方式,模块的包需要init.py文件,没有内容就空文件)
      • my.py
      • 注意:为了更好理解子包安装,这里画蛇添足的增加了有个子包mypkg
    • 测试的脚本代码
      • test.py
setup.py需要安装内容的路径

安装脚本文件setup.py

from distutils.core import setup, Extension
setup(
    name="mydemos",  # 用于pip安装中指定的名字
    version="1.1",
    packages=["mydemo", "mydemo.mypkg"],   # 包路径下一定要有__init__.py
    scripts=["test.py"],
    ext_modules=[
        Extension("mymodule", sources=["C_ython.c", "C.c"],language="C"),    # 需要安装的python扩展模块
    ]
)

直接安装

  • 命令:python setup.py install

  • 安装过程

安装过程截图
  • 安装结果
    1. Python的C扩展模块
    2. Python包与模块
安装结果
  • 使用pip list也能查看到安装的模块
pip list查看
  • 执行测试
    • 注意使用包的方式,访问扩展模块就需要多几个包了,下面是在IPython环境下测试,在非交互式编程也是一样的。
使用安装好的模块

安装打包

  • 命令:python setup.py sdist
制作安装包的过程
  • 使用安装就非常简单了,直接解压,然后调用python setup.py install就ok。

附录

  • 如果包路径比较分散:可以使用:package_dir选项配置。这里就不举例子了。

  • requires可以指定依赖模块。

  • data_files提供数据文件。


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