Cython入门

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/weixin_36670529/article/details/102793670

setup这一年也是遇到了很多次,随着python编程学习的不断深度对于python的了解也不断在增加,这里做一次简单的小节。

相关工具:distutils,cython

1.Cython简介

我们平时使用的python,又叫CPython,因为他是用C语言写的,一般来说,我们的python源代码(.py沃森件),首先编译成字节码(.pyc文件),然后将.pyc文件放在python虚拟机上运行,这里的python虚拟机就是所谓的“python解释器”。总而言之,纯python代码的运行速度介于传统的编译语言和传统的解释语言之间。

2.1什么是字节码?

字节码在python虚拟机程序里对应的是PyCodeObject对象; .pyc文件是字节码在磁盘上的表现形式。

每一个以py结尾的python源代码文件都是模块,其中那个启动后能够运行整个程序的文件叫顶层文件。而顶层文件导入其他模块(文件),必须找到文件然后将其编译成字节码,并且运行字节码。

导入文件时编译的字节码会自动保存,同时保存的还有时间戳。如果同时存在.py和.pyc,python会使用.pyc运行,如果.pyc的编译时间早于.py的时间,则重新编译.py,并更新.pyc文件。

如果python无法在机器上写入字节码,程序仍然可以工作,字节码会在内存中生成并在程序结束时丢弃掉。(严格而讲,只有文件导入的情况下字节码才会保存,并不是对顶层文件)。

如果想生成test.pyc,我们可以使用python内置模块py_compile来编译。也可以执行命令 python -m test.py 这样,就生成了test.pyc。

typedef struct {
    PyObject_HEAD
    int co_argcount;        /* 位置参数个数 */
    int co_nlocals;         /* 局部变量个数 */
    int co_stacksize;       /* 栈大小 */
    int co_flags;   
    PyObject *co_code;      /* 字节码指令序列 */
    PyObject *co_consts;    /* 所有常量集合 */
    PyObject *co_names;     /* 所有符号名称集合 */
    PyObject *co_varnames;  /* 局部变量名称集合 */
    PyObject *co_freevars;  /* 闭包用的变量名集合 */
    PyObject *co_cellvars;  /* 内部嵌套函数引用的变量名集合 */
    /* The rest doesn’t count for hash/cmp */
    PyObject *co_filename;  /* 代码所在文件名 */
    PyObject *co_name;      /* 模块名|函数名|类名 */
    int co_firstlineno;     /* 代码块在文件中的起始行号 */
    PyObject *co_lnotab;    /* 字节码指令和行号的对应关系 */
    void *co_zombieframe;   /* for optimization only (see frameobject.c) */
} PyCodeObject;

2.2执行字节码?

Python虚拟机的原理就是模拟可执行程序再X86机器上的运行,X86的运行时栈帧如下图:当发生函数调用时,创建新的栈帧,对应Python的实现就是PyFrameObject对象。PyFrameObject对象创建程序运行时的动态信息,即执行环境,相关源码大致如下:

typedef struct _frame{  
    PyObject_VAR_HEAD //"运行时栈"的大小是不确定的  
    struct _frame *f_back; //执行环境链上的前一个frame,很多个PyFrameObject连接起来形成执行环境链表  
    PyCodeObject *f_code; //PyCodeObject 对象,这个frame就是这个PyCodeObject对象的上下文环境  
    PyObject *f_builtins; //builtin名字空间  
    PyObject *f_globals;  //global名字空间  
    PyObject *f_locals;   //local名字空间  
    PyObject **f_valuestack; //"运行时栈"的栈底位置  
    PyObject **f_stacktop;   //"运行时栈"的栈顶位置  
    //...  
    int f_lasti;  //上一条字节码指令在f_code中的偏移位置  
    int f_lineno; //当前字节码对应的源代码行  
    //...  
      
    //动态内存,维护(局部变量+cell对象集合+free对象集合+运行时栈)所需要的空间  
    PyObject *f_localsplus[1];    
} PyFrameObject;

每一个 PyFrameObject对象都维护了一个 PyCodeObject对象,这表明每一个 PyFrameObject中的动态内存空间对象都和源代码中的一段Code相对应。

2.2什么是Cython?

Cython是Python语言的扩展模块,他的目的在于称为python语言的超集(superset),为python提供高级的,面向对象的,函数式的和动态的编程。他的主要功能是支持(可选)部分静态类型的声明作为Cython语言的一部分。这样cython的源代码就可以被转化为优化过的C/C++代码,然后可以将这些代码编程称为python的扩展模块。Cython代码在CPython运行时环境中执行,但是以编译的C的速度执行,并且能够直接调用C库。同时,它保留了Python源代码的原始接口,这使得它可以直接从Python代码中使用。

2.3构建Cython

Cython代码必须编译,具体包括两步:

  第一步,将A.pyx文件用Cython编译到一个.c文件中,其中含有python扩展模块的代码

  第二步,将.c文件编译成.so文件(Windows下为.pyd文件),该文件可以 直接import进入Python session。Distutils或setuptools负责这部分。虽然Cython可以在某些情况下为你调用它们。

具体实例,在faser rcnn中,bbox_overlaps函数就行用cython写的。

具体构建Cython的几种方法:

1.写一个distutils / setuptools setup.py。

setyp.py:

from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("helloworld.pyx")
)

命令行:

$ python setup.py build_ext --inplace

2.使用Pyximport,导入Cython .pyx文件就像它们是.py文件一样(使用distutils在后台编译和构建)。这种方法比编写一个setup.py容易一些。但不是很灵活,比如,您需要某些编译选项。(其实,没有学习编译原理的我不需要哪些编译选项,显然这种方法是极好的)。但是,我的电脑在使用这种方法的时候,失败了,可能是VS环境的原因吧,所以我采用了第一种方法,直接编译了一个pyd文件使用。

import pyximport; pyximport.install()
import helloworld
# Hello World

    3.Jupyter notebook允许内联Cython代码。这是开始编写Cython代码并运行它的最简单方法。

2.distutils简介

除了Cython中使用到setup.py,编写python的第三方库,也是要编写setup.py的。其实如果我们下载过一些第三库的源代码文件,打开之后一般就会有一个setup.py,执行python setup.py install 就可以安装这个库了。setup.py 如何编写内容很多,可以参考官方文档:https://wiki.python.org/moin/Distutils/Tutorial?highlight=%28setup.py%29。一个典型的setup.py的写法如下(参考自官方文档):

文件结构为: top |-- package | |-- __init__.py # 空的 | |-- module.py # 这个package中的模块文件 | `-- things | |-- cross.png | |-- fplogo.png | `-- tick.png |-- README `-- setup.py

setup.py:

from distutils.core import setup

#This is a list of files to install, and where
#(relative to the 'root' dir, where setup.py is)
#You could be more specific.
files = ["things/*"]

setup(name = "appname",
    version = "100",
    description = "yadda yadda",
    author = "myself and I",
    author_email = "email@someplace.com",
    url = "whatever",
    #Name the folder where your packages live:
    #(If you have other packages (dirs) or modules (py files) then
    #put them into the package directory - they will be found 
    #recursively.)
    packages = ['package'],
    #'package' package must contain files (see list above)
    #I called the package 'package' thus cleverly confusing the whole issue...
    #This dict maps the package name =to=> directories
    #It says, package *needs* these files.
    package_data = {'package' : files },
    #'runner' is in the root.
    scripts = ["runner"],
    long_description = """Really long text here.""" 
    #
    #This next part it for the Cheese Shop, look a little down the page.
    #classifiers = []     
) 

Making the installation tarball

python setup.py sdist

我的编译过程:

setup.py:

import sys
import numpy as np
A=sys.path.insert(0, "..")
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

# ext_module = cythonize("TestOMP.pyx")
ext_module = Extension(
                        "bbox",
            ["bbox.pyx"],
            extra_compile_args=["/openmp"],
            extra_link_args=["/openmp"],
            )

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [ext_module],
    #注意这一句一定要有,不然只编译成C代码,无法编译成pyd文件
    include_dirs=[np.get_include()]
)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏DotNet程序园

刷新:重新发现.NET与未来

微软在比尔·盖茨手中创立并崛起, 成为PC互联网时代的霸主,很多70,80后都有MVP Edi Wang 的体验<.NET成人礼 | 还记得20年前一起拖过的控...

9810
来自专栏微信公众号【Java技术江湖】

夯实Java基础系列14:深入理解Java枚举类

枚举(enum)类型是Java 5新增的特性,它是一种新的类型,允许用常量来表示特定的数据片断,而且全部都以类型安全的形式来表示。

11320
来自专栏网络技术联盟站

JAVA基础 | java基础7(字符串)

String类、StringBuffer类和StringBuilder类 String类和StringBuffer类都在Java的lang包中,并被定义为最终(...

10020
来自专栏java一日一条

推荐11个挺常用的IDEA插件

今天介绍一下IDEA的一些炫酷且好用的插件,IDEA强大的插件库,不仅能给我们带来一些开发的便捷,还能体现我们的与众不同。

13620
来自专栏微信公众号【Java技术江湖】

夯实Java基础系列15:Java注解简介和最佳实践

Annotation 中文译过来就是注解、标释的意思,在 Java 中注解是一个很重要的知识点,但经常还是有点让新手不容易理解。

6610
来自专栏算法与编程之美

Java|Java中两种抛出异常的方式

在Java中有两种抛出异常的方式,一种是throw,直接抛出异常,另一种是throws,间接抛出异常。

13110
来自专栏网络技术联盟站

JAVA基础 | 谈一谈枚举

1.私有化类的构造器,保证不能在类的外部创建其对象 2.在类的内部创建枚举类的实例。声明为: public static final 3.对象如果有实例变量,应...

13120
来自专栏微信公众号【Java技术江湖】

夯实Java基础系列12:深入理解Java中的反射机制

反射(Reflection)是Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。Oracle官方对...

9210
来自专栏DotNet Core圈圈

刷新:重新发现.NET与未来

当时的微软对我来说就是神的存在。因为我认识电脑到使用电脑的一切几乎都离不开这家伟大的公司,它就像上帝一样开天辟地,创造万物:Windows、Office、IE这...

7910
来自专栏分布式+云运维技术专栏

Linux安装JDK

如果您有更好的技术与作者分享,或者商业合作; 请访问作者个人网站 http://www.esqabc.com/view/message.html 留言给...

16520

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励