『Python学习笔记』使用Cython编程语言编译python文件

news2024/12/23 10:33:02
使用Cython编程语言编译python文件

文章目录

  • 一. Cython简介
  • 一. Cython编译
    • 2.1. 编译过程
    • 2.2. 环境安装
    • 2.3. disutils库
    • 2.4. 引入C源文件
  • 三. 总结
  • 参考文献

一. Cython简介

  • Cython官网地址:https://cython.org/
  • Cython的下载和安装:https://pypi.org/project/Cython/
  • Cython是一个快速生成Python扩展模块的工具,从语法层面上来讲是 Python语法和C语言语法的混血,当Python性能遇到瓶颈时,Cython直接将C的原生速度植入Python程序,这样使Python程序无需使用C重写,能快速整合原有的Python程序,这样使得开发效率和执行效率都有很大的提高,而这些中间的部分,都是Cython帮我们做了。

一. Cython编译

  • 因为Cython是 Python 的超集,所以 Python 解释器无法直接运行 Cython 的代码,那么如何才能将 Cython 代码变成 Python 解释器可以识别的有效代码呢?答案是通过 Cython 编译 Pipeline。
  • Pipeline 的职责就是将 Cython 代码转换成 Python 解释器可以直接导入并使用的 Python 扩展模块,这个 Pipeline 可以在不受用户干预的情况下自动运行(使 Cython 感觉像 Python 一样),也可以在需要更多控制时由用户显式的运行。

2.1. 编译过程

  • Pipeline由两步组成
  • 第一步 是由 cython 编译器负责将 Cython 转换成经过优化并且依赖当前平台的 C、C++ 代码;
  • 第二步 是使用标准的 C、C++ 编译器将第一步得到的 C、C++ 代码进行编译并生成标准的扩展模块,并且这个扩展模块是依赖特定的平台的。如果是在 Linux 或者 Mac OS,那么得到的扩展模块的后缀名为 .so如果是在 Windows 平台,那么得到的扩展模块的后缀名为 .pyd(扩展模块 .pyd 本质上是一个 DLL 文件)。不管是什么平台,最终得到的都会是一个成熟的 Python 扩展模块,它是可以直接被 Python 解释器进行 import 的。
  • 注意: Cython编译器是一种源到源的编译器,并且生成的扩展模块也是经过高度优化的,因此Cython生成的C代码编译得到的扩展模块比手写的C代码编译得到的扩展模块运行的要快并不是一件稀奇的事情。因为 Cython 生成的 C 代码是经过高度精炼,所以大部分情况下比手写所使用的算法更优,而且 Cython 生成的 C 代码支持所有的通用 C 编译器,生成的扩展模块同时支持许多不同的 Python 版本。

2.2. 环境安装

  • 现在我们知道在编译 Pipeline 中有两个步骤,而实现这两个步骤需要我们确保机器上有 C、C++ 编译器以及 Cython 编译器,不同的平台有不同的选择。
  • C、C++编译器: Linux 和 Mac OS无需多说,因为它们都自带 gcc,但是注意:如果是 Linux 的话,我们还需要 yum install python3-devel(以 CentOS 为例)。至于 Windows,可以下载一个 Visual Studio,但是那个玩意会比较大,如果不想下载 vs 的话,那么可以选择安装一个 MinGW 并设置到环境变量中,至于下载方式可以去https://sourceforge.net/projects/mingw/files/ 进行下载。
  • 安装cython编译器 安装 cython 编译器的话,可以直接通过 pip install cython 即可。因此我们看到 cython 编译器只是 Python 的一个第三方包,因此运行 Cython 代码同样要借助 Python 解释器。
# 方式1
(allennlp_zkf) zkf@ubuntu:/data/aibox/kaifang/trans/cython$ cython -V
Cython version 0.29.32

# 方式2
import Cython
print(Cython.__version__)
# 0.29.32

2.3. disutils库

  • Python有一个标准库disutils可以用来构建、打包、分发 Python 工程。而其中一个对我们有用的特性就是 它可以借助C编译器将C源码编译成扩展模块,并且这个模块是自带的、考虑了平台、架构、Python 版本等因素,因此我们在任意地方使用disutils都可以得到扩展模块。
  • 注意:上面 disutils 只是帮我们完成了 Pipeline 的第二步,那第一步呢?第一步则是需要 cython 来完成。
  • 斐波那契数列,指的是这样一个数列:1、1、2、3、5、8、13、21、34、55、89
def fib(n):
    """ 这是一个扩展模块 """
    cdef int i
    cdef double a = 0.0, b = 1.0
    for i in range(n):
        a, b = a + b, a
    return a  # 最后一个数字
  • 然后我们对其进行编译:
from distutils.core import setup
from Cython.Build import cythonize

'''
我们说构建扩展模块的过程分为两步
1. 将Cython代码翻译成C代码; 
2. 根据C代码生成扩展模块.
第一步要由cython编译器完成, 通过cythonize;
第二步要由distutils完成, 通过distutils.core下的setup

'''
# 里面的 language_level=3 表示只需要兼容python3即可, 而默认是2和3都兼容
# 强烈建议加上这个参数, 因为目前为止我们只需要考虑python3即可
setup(ext_modules=cythonize("fib.pyx", language_level=3))

'''
cythonize负责将Cython代码转成C代码, 这里我们可以传入单个文件, 也可以是多个文件组成的列表
或者一个glob模式, 会匹配满足模式的所有Cython文件; 然后setup根据C代码生成扩展模块
'''
  • 编译后产生的文件: 这个文件叫做 setup.py,这里只是做了准备,但是还没有进行编译。我们需要终端执行 python setup.py build 进行编译。在我们执行命令之后,当前目录会多出一个build目录,里面的结构如下。重点是那个fib.cpython-37m-x86_64-linux-gnu.so(windows系统的话就是.pyd结尾的)文件,该文件就是根据 fib.pyx 生成的扩展模块,至于其它的可以直接删掉了。我们把这个文件单独拿出来测试一下:

在这里插入图片描述

import fib
import traceback

print(fib)  # <module 'fib' from '/data/aibox/kaifang/trans/cython/fib.so'>

# try:
#     # 我们在里面定义了一个fib函数, 在fib.so里面定义的函数在编译成扩展模块之后可以直接使用
#     print(fib.fib("xx"))  # 6765.0
#
#
# except Exception:
#     print(traceback.format_exc())

# 因为我们定义的是fib(int n), 而传入的不是整型, 所以直接报错
print(fib.fib(20))  # 6765.0

# 我们的注释
print(fib.fib.__doc__)
# 这是一个扩展模块
<module 'fib' from '/data/aibox/kaifang/trans/cython/fib.so'>
Traceback (most recent call last):
  File "/data/aibox/kaifang/trans/cython/test_fib.py", line 16, in <module>
    print(fib.fib("xx"))  # 6765.0
  File "fib.pyx", line 14, in fib.fib
    for i in range(n):
TypeError: an integer is required

55.0
 这是一个扩展模块 

2.4. 引入C源文件

  • 除此之外我们还可以嵌入 C、C++ 的代码,我们来看一下。
// cfib.h
double cfib(int n);  // 定义一个函数声明



//cfib.c
double cfib(int n) {
    int i;
    double a=0.0, b=1.0, tmp;
    for (i=0; i<n; ++i) {
        tmp = a; a = a + b; b = tmp;
    }
   return a;
} // 函数体的实现
  • 然后是.pyx文件:
# 通过 cdef extern from 导入头文件, 写上里面的函数
cdef extern from "cfib.h":
    double cfib(int n)

# 然后 Cython 可以直接调用
def fib_with_c(n):
    """调用 C 编写的斐波那契数列"""
    return cfib(n)
  • 最后是编译:
from distutils.core import setup, Extension
from Cython.Build import cythonize

# 我们看到之前是直接往 cythonize 里面传入一个文件名即可
# 但是现在我们传入了一个 Extension 对象, 通过 Extension 对象的方式可以实现更多功能
# 这里指定的 name 表示编译之后的文件名, 显然编译之后会得到 wrapper_cfib.cp38-win_amd64.pyd
# 如果是之前的方式, 那么得到的就是 fib.cp38-win_amd64.pyd, 默认会和 .pyx 文件名保持一致, 这里我们可以自己指定
# sources 则是代表源文件, 这里我们只需要指定 pyx 和 c 源文件即可, 因为头文件也在同一个目录中
# 如果不在, 那么还需要通过 include_dirs 指定头文件的所在目录, 不然 extern from "cfib.h" 就报错了
ext = Extension(name="wrapper_cfib", sources=["fib.pyx", "cfib.c"])
setup(ext_modules=cythonize(ext))

在这里插入图片描述

  • 然后我们来调用一下:
# !/usr/bin/env python
# -*- encoding: utf-8 -*-
import wrapper_cfib

print(wrapper_cfib.fib_with_c(20))  # 6765.0
print(wrapper_cfib.fib_with_c.__doc__)  # 调用 C 编写的斐波那契数列
6765.0
调用 C 编写的斐波那契数列

Process finished with exit code 0
  • 我们看到成功调用 C 编写的斐波那契数列,这里我们使用了一种新的创建扩展模块的方法,我们来总结一下。
  • 如果是单个pyx文件的话, 那么直接通过 cythonize("xxx.pyx") 即可;
  • 如果 pyx 文件还引入了 C 文件, 那么通过 cythonize(Extension(name="xx", sources=["", ""])) 的方式即可;name 是编译之后的扩展模块的名字, sources 是你要编译的源文件, 我们这里是一个 pyx 文件一个 C 文件;
  • 建议后续都使用第二种方式,可定制性更强,而且我们之前使用的 cythonize("fib.pyx") 完全可以用 cythonize(Extension("fib", ["fib.pyx"])) 进行替代。

三. 总结

  • 目前我们介绍了如何将 pyx 文件编译成扩展模块,对于一个简单的 pyx 文件来说,方法如下:
from distutils.core import setup, Extension
from Cython.Build import cythonize

# 推荐以后就使用这种方法
ext = Extension(
    name="wrapper_fib",  # 生成的扩展模块的名字
    sources=["fib.pyx"],  # 源文件
)
setup(ext_modules=cythonize(ext, language_level=3))  # 指定Python3

参考文献

  • Cython官网地址:https://cython.org/
  • 《Cython系列》2.编译并运行 Cython 代码的几种方式:https://www.cnblogs.com/traditional/p/13213173.html
  • https://www.cnblogs.com/traditional/
  • https://www.jianshu.com/p/9053dacee822

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式分享合集117

一、获取STM32代码运行时间的技巧 测试代码的运行时间的两种方法&#xff1a; 使用单片机内部定时器&#xff0c;在待测程序段的开始启动定时器&#xff0c;在待测程序段的结尾关闭定时器。为了测量的准确性&#xff0c;要进行多次测量&#xff0c;并进行平均取值。 借助示波器…

NFV概述

NFV&#xff08;网络功能虚拟化&#xff09;是指利用虚拟化技术在标准化的通用IT设备&#xff08;X86服务器&#xff0c;存储和交换设备&#xff09;上实现各种网络功能。NFV的目标是取代通信网络中私有、专用和封闭的网元&#xff0c;实现统一通用硬件平台业务逻辑软件的开放架…

争议不断的AI绘画,靠什么成为了顶流?

今年以来&#xff0c;AIGC迅速崛起。所谓AIGC&#xff0c;即AI-Generated Content&#xff0c;指的是利用人工智能来生成内容&#xff0c;被认为是继专业产出内容&#xff08;PGC&#xff09;、用户产出内容&#xff08;UGC&#xff09;后的新型内容创作方式。不久前掀起热议的…

客户需求太多,如何有效沟通完成项目?

1、向客户明确&#xff1a;工作量、时间与质量的关系 需要想客户明确&#xff0c;某时间内在保障开发质量的前提下&#xff0c;实际的工作量。如果加大工作量&#xff0c;在赶工情况下&#xff0c;开发质量无法保障。如要保障开发质量&#xff0c;开发时间会延长&#xff0c;那…

【电力系统】基于Matlab实现风电光伏概率潮流计算

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

threejs官方demo学习(1):animation

前言 之前的threejs入门视频教学已经学习完了&#xff0c;下面会陆续学习官方demo。官方网址太卡了建议在本地进行搭建&#xff0c;具体见&#xff1a;threejs视频教程学习&#xff08;1&#xff09;&#xff1a;本地搭建threeJS官网、渲染第一个场景 官方的例子都是html格式…

Qt源码编译aarch等架构可参考

前言&#xff1a; 我的建议是&#xff1a; 编译环境一定要有网络&#xff0c;依赖的包才可以方便安装&#xff01;&#xff01;&#xff01; 一、准备工作 有网但ping不通、或者没有源的 可能需要先配置: 源路径&#xff1a; /etc/apt/source.list 系统默认DNS配置: /etc/r…

iTOP3588开发板编译Android内核方法一

iTOP3588开发板编译Android内核方法一&#xff1a; 在 Android 源码目录下执行如下命令编译 Android 内核&#xff1a; ./build.sh -CKA 编译完成后如下图所示&#xff1a; 编译后会在 rockdev/Image-rk3588_s 目录下生成 boot.img&#xff0c;boot.img 为内核镜像。boot.i…

《操作系统实战 45 讲》系统环境搭建 (Ubuntu + Vmware)

系统环境搭建 1. Ubuntu下载2. Vmware安装 Ubuntu系统2.1 配置电脑2.2 安装系统&#xff08;Ubuntu16.04&#xff09;3、配置网络和修改yum源3.1. 编辑 VMware 的网络配置3.2 windows 的网络配置3 .3 虚拟机网络 IP 修改地址配置3.4 修改yum源3.5 安装vm-tools1. Ubuntu下载 清…

【泊车】基于强化学习实现智能泊车附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

仿照string类,实现自定义My_strng类,以及相关操作

代码 #include <iostream> #include <cstring>using namespace std;class My_string { private:char* cstr;int len; public:My_string():cstr(NULL),len(0) //无参构造{}My_string(const char* str) //有参构造{this->len strlen(s…

【C++笔试强训】第二天

选择题 1.使用printf函数打印一个double类型的数据&#xff0c;要求&#xff1a;输出为10进制&#xff0c;输出左对齐30个字符&#xff0c;4位精度。以下哪个选项是正确的&#xff1f; A %-30.4e B %4.30e C %-30.4f D %-4.30f 对于 %m.nf &#xff0c;double对应%f.要求打印…

爬虫之Scrapy架构

目录 Scrapy架构介绍 Scrapy下载 Scrapy基本使用 Scrapy目录结构 Scrapy解析数据 settings相关配置 基础配置 增加爬虫的爬取效率 去重规则&#xff08;布隆过滤器&#xff09; 持久化方案(数据保存) request和response传递参数 网页解析下一页继续爬取 爬虫和下载…

Java安全之深入了解SQL注入

深入了解Java中的SQL注入 本文以代码实例复现了Java中JDBC及Mybatis框架采用预编译和非预编译时可能存在SQL注入的几种情况&#xff0c;并给予修复建议。 JDBC 首先看第一段代码&#xff0c;使用了远古时期的JDBC并且并没有使用预编译。这种简单的字符串拼接就存在SQL注入 …

信息化带来的制造业生产管理系统究竟有哪些作用呢?

制造业是一个现代国家的经济基础&#xff0c;决定着着国家的兴衰存亡。长期以来人们和国家都对制造业给予高度重视&#xff0c;无论是资金投入还是管理的手段和方法的提高&#xff0c;制造业在生产行业中的优势越来越明显。尤其是随着科学技术的快速发展&#xff0c;制造业的现…

uniapp easycom

easycom 是 uniapp 的一种组件自动引入的规则&#xff0c;使用这种规则可以使满足规则的组件无需注册直接使用。 接下来我们来看一眼效果 这里可以看到我并没有进行组件注册而是直接使用了组件&#xff0c;这样的效果就是通过 easycom 的自定义规则来实现的。 来看一眼我的自…

庐山真面目之——LWIP初探

目录 LWIP简介 网络层协议分层模型介绍 LWIP源代码结构 lwip源码文件说明 lwip的contrib包文件说明 以太网接入MCU方案 LWIP结构框图 LWIP简介 lwIP 是 Light Weight&#xff08;轻型&#xff09;IP 协议&#xff0c;有无操作系统的支持都可以运行。lwIP 实现的重点 是在保…

Android Studio compose的简单使用与案例实现

Compose是Android团队与JetBrain大力推动的新一代UI框架&#xff0c;它能够简化安卓界面的开发&#xff0c;让本来繁琐的xml文件写法变为简便的kt文件写法。 其声明式 UI、更简单的自定义、实时且带交互的预览功能更是让安卓开发锦上添花 android compose框架的使用一.前置知识…

vue+elementUI 使用腾讯地图

效果如下 引入地图qqmap 刚开始我是直接用 npm install qqmap&#xff0c;但是好像只有v1版本的&#xff0c;我需要用v2版本的&#xff0c;所以直接使用script标签加载API服务。 文件&#xff1a;/public/index.html <script charset"utf-8" src"https:…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java双笙映画ou5oj

毕业设计也不需要做多高端的程序&#xff0c;毕业设计对于大多数同学来说&#xff0c;为什么感觉到难&#xff0c;最重要的一个原因&#xff0c;那就是理论课到实践课的转变&#xff0c;很多人一下不适应&#xff0c;本能开始拒绝&#xff0c;如果是一个考试&#xff0c;大家都…