方法一:C++ 扩展 + PyBind11
首先编写 C++ 核心函数,然后利用PyBind11将其包装,充分发挥C++的高性能和Python的易用性。除此之外,pybind11还能在C++程序中嵌入Python解释器并调用Python函数。最后通过setuptools将其编译成扩展模块,在使用时导入即可。该过程等同于导入一个自己编写的函数,可以实现数十倍的性能提升。备注:setuptools是Python编程语言的一个扩展包管理工具,它构建并安装Python包,提供了一种标准的方式来处理包的依赖关系。一个编译脚本示例如下:
# setup.py
from setuptools import setup, Extension
import pybind11
import sys
if sys.platform == 'win32':
extra_compile_args = ['/O2', '/std:c++17', '/utf-8']
else:
extra_compile_args = ['-O3', '-std=c++17']
ext = Extension(
'floodfill_cpp',
sources=['floodfill.cpp'],
include_dirs=[pybind11.get_include()],
language='c++',
extra_compile_args=extra_compile_args
)
setup(name='floodfill_cpp', ext_modules=[ext])
在命令行或终端中执行如下命令,进行清理和编译:
# 清理
python setup.py clean --all
# 编译
python setup.py build_ext --inplace
方法二:Cython(纯 Python 式编译)
优点是语法接近 Python,学习曲线较平缓,并且可以直接使用 Python中的对象。缺点是性能略低于纯 C++(通常差距在 10~20%),并且仍然需要编译setuptools。仅需要导入cython这个库即可。我的意见是,既然要学习一些新东西,为什么不直接学习更高效的C++呢?而且如果已经有一门编程语言作为基础,那么学习另一门只需要了解它的不同的足够了,毕竟又不是要切换自己的主力编程语言。一个cython的简单示例如下:
# floodfill.pyx
import cython
import numpy as np
cimport numpy as np
from libcpp.queue cimport priority_queue
from libcpp.vector cimport vector
from libcpp.utility cimport pair
@cython.boundscheck(False)
@cython.wraparound(False)
def flood_fill_cython(double[:,:] dem, int start_row, int start_col, double target_volume):
cdef int rows = dem.shape[0]
cdef int cols = dem.shape[1]
cdef double[:,:] depth = np.zeros((rows, cols), dtype=np.float64)
cdef double[:,:] new_dem = np.copy(dem)
cdef priority_queue[pair[double, int], vector[pair[double, int]], greater[pair[double, int]]] pq
cdef vector[char] processed = vector[char](rows*cols, 0)
cdef vector[int] flooded_indices
cdef vector[double] flooded_elevs
cdef int start_idx = start_row * cols + start_col
pq.push(pair[double, int](dem[start_row][start_col], start_idx))
cdef double current_water_level = dem[start_row][start_col]
cdef double current_volume = 0.0
cdef long total_area = 0
cdef int dr[8] = [-1,-1,-1,0,0,1,1,1]
cdef int dc[8] = [-1,0,1,-1,1,-1,0,1]
while target_volume > current_volume and not pq.empty():
cdef double elev = pq.top().first
cdef int idx = pq.top().second
pq.pop()
C++基础(C++ Primier Plus)