C++开发(五)

C++和Python开发(五)

最后整理些细碎的内容,就把和GPT的聊天都删掉了,最近在这些上投入了太多精力,需要做些其他事情缓解一下了。

Pytest

pytest中的tests目录下可以新建一个conftest.py文件,不同的test_xx.py都会先运行这个文件,类似于GoogleTest中的夹具。

import

Python的import确实还有很多地方我没弄明白。from xx import yy中,xx必须是一个实际存在的东西,不论是一个目录(包)还是一个文件(模块),纯粹导入命名空间是不行的。

setup

可以通过环境变量,让用户自行决定是否安装某些扩展模块。这可以通过扩展build_ext来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class CMakeBuild(build_ext):
def run(self):
super().run()
cmake_build = os.environ.get('USE_CMAKE', 'False').lower() in ('true', '1', 'yes')
if cmake_build:
self.build_with_cmake()
else:
pass

def build_with_cmake(self):
# 创建构建目录
build_temp = os.path.join(self.build_temp, 'cmake_build')
os.makedirs(build_temp, exist_ok=True)

# 配置 CMake
subprocess.check_call(['cmake', os.path.abspath(os.getcwd())], cwd=build_temp)
subprocess.check_call(['cmake', '--build', '.'], cwd=build_temp)

# 找到生成的 .so 或 .pyd 文件并将其复制到目标目录
for file in os.listdir(build_temp):
if file.endswith(('.so', '.pyd')):
self.copy_file(os.path.join(build_temp, file), self.get_ext_fullpath(file))

pip和python setup.py

  1. pip install .
  • 依赖管理: pip 会自动管理依赖关系,并确保所有依赖项都被正确安装。如果你的项目中定义了 install_requires 选项(在 setup.py 中),pip 会确保这些依赖项在安装你的包之前被安装。
  • 使用 wheel: 如果可能,pip 会尝试使用已经构建好的 wheel 文件(.whl)来安装包,这通常比从源码构建快得多。如果没有现成的 wheelpip 也可以从源码构建并安装。
  • 隔离环境: 默认情况下,pip 会使用一个临时的隔离环境来执行构建,这意味着构建过程中使用的依赖项不会污染全局的Python环境。可以通过 --no-build-isolation 选项来禁用这一行为。
  • 自动安装 setuptoolswheel: pip 会自动确保 setuptoolswheel 这些构建工具已经安装。
  • 更多高级功能: pip 提供了许多额外的功能,比如 --upgrade(升级已安装的包)、--target(指定安装目录)等。
  1. python setup.py install
  • 手动安装: 直接调用 setup.pyinstall 命令,不会管理依赖项。这意味着你需要手动确保所有依赖项都已经安装。
  • 不使用 wheel: setup.py install 直接从源码构建并安装包,而不是使用 wheel 文件。因此,安装速度可能会更慢,尤其是对于需要编译的扩展模块。
  • 无隔离环境: setup.py install 不会在隔离的环境中运行,所有构建和安装操作都直接在当前的 Python 环境中进行。这可能会导致依赖项之间的冲突,或者污染全局的 Python 环境。
  • 逐步过时: python setup.py install 的使用在逐渐减少,因为 pip 提供了更多的功能和更好的依赖管理。官方文档也建议使用 pip 来进行安装。

关于inplace

当运行 python setup.py build_ext 来构建 Cython 模块后,生成的 .pyd(Windows)或 .so(Linux/Mac)文件通常会被放置在一个特定的构建目录中。默认情况下,这些文件可能并没有直接放在你的源代码目录中,而是被放置在一个名为 build 的子目录里。

  1. 查看 build 目录

    • 默认情况下,构建的模块通常位于 build/lib.<platform> 目录下。例如,在 Windows 上可能是 build/lib.win-amd64-3.8(假设你使用的是 Python 3.8)。

    • 你可以使用以下命令来查看构建目录:

      1
      ls build/
  2. 使用 --inplace 选项

    • 如果你希望生成的模块直接放在源代码目录(与 .pyx 文件相同的目录)中,可以在运行 build_ext 时使用 --inplace 选项:

      1
      python setup.py build_ext --inplace
    • 这样生成的 .pyd 文件将直接出现在源代码目录中,与你的 .pyx 文件在同一个位置。

  3. 指定构建目录

    • 如果你希望构建的模块放在一个特定的目录中,可以在 setup.py 中使用 ext_modulesExtension 类的参数 build_dir 来指定。

关于cython构建

cython的构建比较麻烦,在命令行里直接用cythonize,至少在Win下很难导入Numpy的头文件。在setup.py里改写build_extrun方法,cython的构建就会一直出错。现在看来比较好的方式就是,ext_modules中只放置cython的模块,重写build_ext后,调用super().run(),保证cython模块的正常构建。其他模块则在之后cmake手动构建,但这些模块都不放入到ext_modules中。构建出来的文件,不需要手动改写RECORD文件注册,直接放到对应的package_data参数中就可以了。这样的方式目前来看是可行的,而且干净的。

关于相对导入

Python的相对导入只能是一个大包下面的包或者模块间相对导入,外部包间是不支持的。