You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Python调用多个含不同大小可分配数组的Fortran函数实例

嘿,这个需求我之前帮人处理过类似的,核心是要让每个Fortran实例拥有独立的内存空间对吧?毕竟不同实例的数组大小不一样,不能共用一块内存。下面给你一套可行的方案:

步骤1:重构Fortran代码为实例化模式

首先得把原来的全局数组和状态变量封装到派生类型里,这样每个实例都能拥有自己的独立副本,不会互相干扰。举个简化的示例:

! module my_fortran_code.f90
module my_module
    implicit none

    ! 定义实例类型,包含所有需要的可分配数组和状态
    type :: FortranInstance
        real, allocatable :: array1(:)
        real, allocatable :: array2(:,:)
        ! 其他业务相关的变量都放这里
    end type FortranInstance

contains

    ! 初始化实例:传入数组大小,完成内存分配
    subroutine c_init_instance(instance, n1, n2)
        type(FortranInstance), intent(out) :: instance
        integer, intent(in) :: n1, n2

        allocate(instance%array1(n1))
        allocate(instance%array2(n1, n2))
        ! 这里可以加你的初始化逻辑,比如数组置零、参数赋值等
    end subroutine c_init_instance

    ! 示例计算函数:操作当前实例的数组
    subroutine c_compute(instance, input, output, input_len)
        type(FortranInstance), intent(inout) :: instance
        real, intent(in) :: input(:)
        real, intent(out) :: output(:)
        integer, intent(in) :: input_len

        ! 替换成你的实际计算逻辑
        output = instance%array1 * input + sum(instance%array2, dim=2)
    end subroutine c_compute

    ! 销毁实例:释放已分配的内存
    subroutine c_destroy_instance(instance)
        type(FortranInstance), intent(inout) :: instance

        if (allocated(instance%array1)) deallocate(instance%array1)
        if (allocated(instance%array2)) deallocate(instance%array2)
        ! 其他需要清理的资源也在这里处理
    end subroutine c_destroy_instance

end module my_module
步骤2:更新Cython封装代码

接下来要在Cython里把Fortran的派生类型封装成Python类,这样Python就能轻松创建多个实例了。

首先写.pxd文件(用来声明Fortran接口,相当于头文件):

# my_fortran.pxd
cdef extern from "my_fortran_code.mod":
    ctypedef struct FortranInstance:
        pass  # Cython不需要知道内部结构,当作不透明指针处理就行

    void c_init_instance(FortranInstance *instance, int n1, int n2)
    void c_compute(FortranInstance *instance, double *input, double *output, int input_len)
    void c_destroy_instance(FortranInstance *instance)

然后写.pyx文件,封装成Python可调用的类:

# my_wrapper.pyx
import numpy as np
cimport numpy as np
from my_fortran cimport *

cdef class FortranCalculator:
    cdef FortranInstance instance  # 存储Fortran实例的内部状态

    def __cinit__(self, int n1, int n2):
        # 构造时初始化Fortran实例
        c_init_instance(&self.instance, n1, n2)

    def compute(self, np.ndarray[np.double_t, ndim=1] input):
        # 准备输出数组(根据你的业务调整大小)
        cdef np.ndarray[np.double_t, ndim=1] output = np.empty_like(input)
        # 调用Fortran计算函数
        c_compute(&self.instance, <double*>input.data, <double*>output.data, input.shape[0])
        return output

    def __dealloc__(self):
        # 销毁时释放Fortran实例的内存,避免泄漏
        c_destroy_instance(&self.instance)

⚠️ 注意:如果你的Fortran代码用的是列优先内存布局(Fortran默认),而numpy默认是行优先,传递多维数组时要记得用np.asarray(input, order='F')转换,避免数据错乱。

步骤3:编译与测试

用setup.py把Fortran和Cython代码一起编译成Python扩展:

# setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize
import numpy as np

fortran_ext = Extension(
    'my_wrapper',
    sources=['my_wrapper.pyx', 'my_fortran_code.f90'],
    extra_compile_args={'fortran': ['-O2']},  # Fortran编译优化
    include_dirs=[np.get_include()],
    language='c'
)

setup(
    name='my_fortran_wrapper',
    ext_modules=cythonize([fortran_ext], compiler_directives={'language_level': "3"})
)

执行编译命令:

python setup.py build_ext --inplace

最后在Python里测试多实例:

# test.py
import numpy as np
import my_wrapper

# 创建两个不同数组大小的独立实例
calc_small = my_wrapper.FortranCalculator(n1=10, n2=5)
calc_large = my_wrapper.FortranCalculator(n1=50, n2=20)

# 分别调用计算,结果完全独立
input_small = np.random.rand(10)
output_small = calc_small.compute(input_small)

input_large = np.random.rand(50)
output_large = calc_large.compute(input_large)

print("小实例输出形状:", output_small.shape)
print("大实例输出形状:", output_large.shape)

# 手动销毁实例(或者等Python垃圾回收自动处理)
del calc_small, calc_large
额外注意事项
  • 错误处理:可以在Fortran里添加内存分配失败的检查,在Cython里用except -1把错误传递给Python,让代码更健壮;
  • 编译器兼容性:如果用的是Intel Fortran编译器,接口声明可能需要微调,但GCC(gfortran)和Cython的配合是最顺畅的;
  • 性能优化:如果需要极致性能,可以在Cython里关闭边界检查(cimport cython后加@cython.boundscheck(False)),但要确保数组索引不会越界。

内容的提问来源于stack exchange,提问作者Andrés Asensio Ramos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:17:49