Cython技术问询:如何从Geometry包顶层直接导入多个类?
问题描述
当前文件结构
CPP Folder Cython Folder ├── setup.py └── Geometry ├── Circle │ ├── __init__.py │ ├── Circle.pyx │ ├── Circle.pyi │ └── Circle.pxd └── Point ├── __init__.py ├── Point.pyx ├── Point.pyi └── Point.pxd
当前setup.py内容
from setuptools import setup, Extension, find_packages from Cython.Build import cythonize point_extension = Extension( "Geometry.Point.Point", [ "src/Geometry/Point/Point.pyx", "../cpp/lib/src/Point.cpp" ], include_dirs=[ "../cpp/lib/include" ], libraries=["Some Library"], library_dirs=[ "src/Geometry" ], extra_compile_args=["-std=c++17", "-O3"], language="c++" ) circle_extension = Extension( "Geometry.Circle.Circle", [ "../cpp/lib/src/Circle.cpp", "src/Geometry/Circle/Circle.pyx", "../cpp/lib/src/Point.cpp" ], include_dirs=[ "../cpp/lib/include" ], libraries=["Some Library"], library_dirs=[ "src/Geometry" ], extra_compile_args=["-std=c++17", "-O3"], language="c++" ) setup( name="Geometry", version="0.1", packages=find_packages(where="src"), package_dir={"": "src"}, package_data={ "Geometry.Circle": ["*.so", "*.pyi"], "Geometry.Point": ["*.so", "*.pyi"] }, ext_modules=cythonize([point_extension, circle_extension], compiler_directives={"language_level": 3}, include_path=[ "../../Expression/cython/src/Some Library", "src/Geometry", ], annotate=True), zip_safe=False, )
当前导入方式
from Geometry.Point import Point from Geometry.Circle import Circle
目标与设想文件结构
目标是实现from Geometry import Circle, Point的导入方式,调整后的文件结构如下:
CPP Folder Cython Folder ├── setup.py └── Geometry ├── __init__.py ├── Geometry.pyx ├── Geometry.pyi ├── Geometry.pxd ├── Circle │ ├── __init__.py │ ├── Circle.pyx │ ├── Circle.pyi │ └── Circle.pxd └── Point ├── __init__.py ├── Point.pyx ├── Point.pyi └── Point.pxd
提问
如何重写setup.py,以及编写Geometry.pxd、.pyx和.pyi文件?
附Point示例文件
Point.pxd
from libcpp.memory cimport shared_ptr, weak_ptr, make_shared from Bryan cimport _Bryan, Bryan cdef extern from "Point.h": cdef cppclass _Point: _Point(shared_ptr[_Bryan] x, shared_ptr[_Bryan] y, shared_ptr[_Bryan] z) shared_ptr[_Bryan] get_x() shared_ptr[_Bryan] get_y() shared_ptr[_Bryan] get_z() cdef class Point: cdef shared_ptr[_Point] c_point
Point.pyx
from Point cimport * cdef class Point: def __cinit__(self, Bryan x=Bryan("0", None), Bryan y=Bryan("0", None), Bryan z=Bryan("0", None)): self.c_point = make_shared[_Point](x.thisptr, y.thisptr, z.thisptr) def __dealloc(self): self.c_point.reset() def get_x(self) -> Bryan: cdef shared_ptr[_Bryan] result = self.c_point.get().get_x() cdef Bryan coord = Bryan("", None, make_with_pointer = True) coord.thisptr = result return coord def get_y(self) -> Bryan: cdef shared_ptr[_Bryan] result = self.c_point.get().get_y() cdef Bryan coord = Bryan("", None, make_with_pointer = True) coord.thisptr = result return coord def get_z(self) -> Bryan: cdef shared_ptr[_Bryan] result = self.c_point.get().get_z() cdef Bryan coord = Bryan("", None, make_with_pointer = True) coord.thisptr = result return coord property x: def __get__(self): return self.get_x() property y: def __get__(self): return self.get_y() property z: def __get__(self): return self.get_z()
解决方案
1. 重写setup.py
新增Geometry.Geometry扩展模块,同时保留原有子模块扩展,调整包数据和路径配置:
from setuptools import setup, Extension, find_packages from Cython.Build import cythonize point_extension = Extension( "Geometry.Point.Point", [ "src/Geometry/Point/Point.pyx", "../cpp/lib/src/Point.cpp" ], include_dirs=[ "../cpp/lib/include" ], libraries=["Some Library"], library_dirs=[ "src/Geometry" ], extra_compile_args=["-std=c++17", "-O3"], language="c++" ) circle_extension = Extension( "Geometry.Circle.Circle", [ "../cpp/lib/src/Circle.cpp", "src/Geometry/Circle/Circle.pyx", "../cpp/lib/src/Point.cpp" ], include_dirs=[ "../cpp/lib/include" ], libraries=["Some Library"], library_dirs=[ "src/Geometry" ], extra_compile_args=["-std=c++17", "-O3"], language="c++" ) # 新增根模块扩展 geometry_extension = Extension( "Geometry.Geometry", [ "src/Geometry/Geometry.pyx" ], include_dirs=[ "../cpp/lib/include", "src/Geometry/Point", "src/Geometry/Circle" ], libraries=["Some Library"], library_dirs=[ "src/Geometry" ], extra_compile_args=["-std=c++17", "-O3"], language="c++" ) setup( name="Geometry", version="0.1", packages=find_packages(where="src"), package_dir={"": "src"}, package_data={ "Geometry": ["*.so", "*.pyi"], "Geometry.Circle": ["*.so", "*.pyi"], "Geometry.Point": ["*.so", "*.pyi"] }, ext_modules=cythonize([point_extension, circle_extension, geometry_extension], compiler_directives={"language_level": 3}, include_path=[ "../../Expression/cython/src/Some Library", "src/Geometry", "src/Geometry/Point", "src/Geometry/Circle" ], annotate=True), zip_safe=False, )
2. 编写Geometry.pxd
声明Cython层面的类型引用,方便根模块调用子模块的cdef类:
# 导入子模块的cdef类定义 from Geometry.Point.Point cimport Point as _Point from Geometry.Circle.Circle cimport Circle as _Circle # 对外暴露根模块的cdef类声明 cdef class Point: cdef _Point inner_point cdef class Circle: cdef _Circle inner_circle
3. 编写Geometry.pyx
封装子模块的类并对外暴露:
from Geometry.Point.Point import Point as PointImpl from Geometry.Circle.Circle import Circle as CircleImpl from Geometry.Geometry cimport Point, Circle # 直接继承子模块类,复用原有实现 cdef class Point(PointImpl): pass cdef class Circle(CircleImpl): pass # 声明对外导出的类 __all__ = ["Point", "Circle"]
4. 编写Geometry.pyi
提供Python静态类型提示:
from Geometry.Point.Point import Point as _Point from Geometry.Circle.Circle import Circle as _Circle class Point(_Point): def __init__(self, x: "Bryan" = ..., y: "Bryan" = ..., z: "Bryan" = ...) -> None: ... @property def x(self) -> "Bryan": ... @property def y(self) -> "Bryan": ... @property def z(self) -> "Bryan": ... def get_x(self) -> "Bryan": ... def get_y(self) -> "Bryan": ... def get_z(self) -> "Bryan": ... class Circle(_Circle): # 根据Circle类实际方法补充类型提示,示例如下 def __init__(self, center: Point, radius: "Bryan" = ...) -> None: ... @property def center(self) -> Point: ... @property def radius(self) -> "Bryan": ... __all__ = ["Point", "Circle"]
5. 编写Geometry目录下的__init__.py
将根模块的类导入,实现直接从Geometry模块导入的效果:
from .Geometry import Point, Circle __all__ = ["Point", "Circle"]
内容的提问来源于stack exchange,提问作者Punreach Rany
相关产品推荐
相关产品推荐

