前提条件:
- 确保电脑已经安装gcc且终端能检索到
- 确保Python中已经安装cython包,若未安装,则先使用pip install cython进行安装
打包方法:
- step1:编写编译脚本setup.py,代码如下:
# encoding = utf-8
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = 'libname',
ext_modules = cythonize(["file1.py", "file2.py", "dir/file3.py"], language_level = "3")
)
- step2:终端中执行:
python3 setup.py build_ext
需要注意的点:
- setup()中的ext_modules不是exe_modules,千万不要打错了,如果打错了,你会发现只编译成了.c文件,而没用生成.so
- 生成的.so只能在打包计算机相同系统、相同平台及相同python版本环境下使用,要想在其他平台或环境下使用,需要在指定平台或环境下重新打包
- 存在多个python版本时,如在linux下博主python2和python3共存,需要指定编译用的python版本,否则终端会报警告FutureWarning: Cython directive ‘language_level’ not set, using 2 for now (Py2)。指定版本有两种方式:
- method 1: 在每个要打包的py文件头部写入# cython:language_level=3
- method 2: 像上述setup.py中一样,在cythonize函数中加入language_level = "3"选项
4.打包完的.so文件结构会被打乱,如上述dir中的file3.py生成的file3.so与其他两个同目录,而不是在dir文件夹下,如果不做调整,执行脚本时可能存在import错误,因此还要手动调整一下文件结构
附批量打包的代码:
import os
import sys
import shutil
import numpy
import tempfile
from setuptools import setup
from setuptools.extension import Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext
import platform
# code from: https://blog.csdn.net/qq_33375598/article/details/118677130
# 构建后存放的文件目录
build_root_dir = 'build/lib.' + platform.system().lower() + '-' + platform.machine() + '-' + str(
sys.version_info.major) + '.' + str(sys.version_info.minor)
print(build_root_dir)
extensions = []
ignore_folders = ['build', 'test', 'tests']
conf_folders = ['conf']
def get_root_path(root):
if os.path.dirname(root) in ['', '.']: # 得到文件的文件路径
return os.path.basename(root) # 返回path最后的文件名
else:
return get_root_path(os.path.dirname(root))
def copy_file(src, dest):
if os.path.exists(dest): # 目的文件存在返回
return
if not os.path.exists(os.path.dirname(dest)): # 目的文件夹不存在,递归创建文件夹
os.makedirs(os.path.dirname(dest))
if os.path.isdir(src): # 判断某一路径是否为目录
shutil.copytree(src, dest) # 拷贝整个文件夹(目的文件夹需要不存在,否则会失败)
else:
shutil.copyfile(src, dest) # 拷贝整个文件
def touch_init_file(): # 在临时文件夹中创建init
init_file_name = os.path.join(tempfile.mkdtemp(), '__init__.py')
with open(init_file_name, 'w'):
pass
return init_file_name
init_file = touch_init_file()
print(init_file)
def compose_extensions(root='.'):
for file_ in os.listdir(root): # 当前目录下的所有文件
abs_file = os.path.join(root, file_) # 路径拼接
if os.path.isfile(abs_file):
if abs_file.endswith('.py'):
extensions.append(Extension(get_root_path(abs_file) + '.*', [abs_file]))
elif abs_file.endswith('.c') or abs_file.endswith('.pyc'):
continue
else:
copy_file(abs_file, os.path.join(build_root_dir, abs_file))
if abs_file.endswith('__init__.py'): # 用空白的__init__.py替代原有的
copy_file(init_file, os.path.join(build_root_dir, abs_file))
else:
if os.path.basename(abs_file) in ignore_folders: # 忽略的文件不拷贝
continue
if os.path.basename(abs_file) in conf_folders: # 配置文件一同拷贝
copy_file(abs_file, os.path.join(build_root_dir, abs_file))
compose_extensions(abs_file)
compose_extensions()
os.remove(init_file)
setup(
name='your_project_name',
version='1.0',
ext_modules=cythonize(
extensions,
nthreads=16,
compiler_directives=dict(always_allow_keywords=True),
include_path=[numpy.get_include()], language_level="3"),
cmdclass=dict(build_ext=build_ext))
参考链接:
- https://blog.csdn.net/qq_33375598/article/details/118677130
- ht
- tps://blog.csdn.net/weixin_36755535/article/details/127300870
评论(0)
您还未登录,请登录后发表或查看评论