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

Cython问题:从引用获取的Numpy数组缺失前两个元素

问题根源:返回局部变量的引用导致悬垂引用

你遇到的这个诡异问题,本质上是C++里一个非常经典的错误——返回了局部变量的引用,这会产生悬垂引用,进而触发未定义行为(也就是你看到的前两个元素变成0的异常情况)。

看你的cmyclass.cc里的get_vec函数:

std::vector<int>& IntVector::get_vec(){
    std::vector<int> buff;
    buff.reserve(vec.size());
    for(int i=0; i<vec.size(); ++i){
        buff.push_back(vec[i]);
    }
    return buff;
}

这里的buff是函数内部的局部变量,当函数执行完毕返回时,这个局部变量会被销毁,它占用的内存会被系统回收或重新分配。你返回的是这个已经被销毁的对象的引用,后续在Cython里访问这个引用指向的内存时,这块内存已经不属于原来的vector了,所以读取到的是随机的垃圾值(也就是你看到的前两个0)。

而当你把返回类型改成std::vector<int>(非引用)时,函数会把局部的buff拷贝一份(C++11及以后会触发更高效的移动语义),返回的是一个全新的、有效的vector对象,所以后续转换成Numpy数组时一切正常。


正确的实现方式:通过参数传入vector引用

你后来采用的「将vector作为参数传入」的方案是完全正确的,这也是C++里避免这类悬垂引用问题的常用手段之一。

修改后的完整代码:

cmyclass.h

#ifndef MYCLASS_H
#define MYCLASS_H
#include <vector>
#include <string>
namespace vec {
class IntVector {
private:
    std::vector<int> vec;
public:
    IntVector();
    virtual ~IntVector();
    void get_vec(std::vector<int>&);
};
}
#endif

cmyclass.cc

#include "cmyclass.h"
#include <iostream>
using namespace vec;
IntVector::IntVector(){
    for(int i=10; i<20; ++i){
        vec.push_back(i);
    }
}
IntVector::~IntVector(){
}
void IntVector::get_vec(std::vector<int>& buff){
    buff.reserve(vec.size());
    for(int i=0; i<vec.size(); ++i){
        buff.push_back(vec[i]);
    }
}

myclass.pyx

import numpy as np
cimport numpy as np
from libcpp.vector cimport vector
cdef extern from "cmyclass.h" namespace "vec":
    cdef cppclass IntVector:
        IntVector() except +
        void get_vec(vector[int]&)
cdef class IntVec:
    cdef IntVector* _thisptr
    def __cinit__(self):
        self._thisptr = new IntVector()
    def __dealloc__(self):
        del self._thisptr
    def __init__(self):
        pass
    def get_vec(self):
        cdef vector[int] buff;
        self._thisptr.get_vec(buff);
        return np.asarray(buff)

setup.py

from distutils.core import setup
from Cython.Build import cythonize
from distutils.extension import Extension
sourcefiles = ['myclass.pyx', 'cmyclass.cc']
compile_opts = ['-std=c++11']
ext=[Extension('*', sourcefiles, extra_compile_args=compile_opts, language='c++')]
setup(
    ext_modules=cythonize(ext)
)

为什么这个方案可行?

这里我们把外部创建的buff(在Cython代码里定义的vector)的引用传入函数,函数内部直接操作这个外部的vector对象。因为buff的生命周期由Cython代码控制(直到转换成Numpy数组之后才会销毁),所以不会出现悬垂引用的问题,读取到的内容都是有效的。

内容的提问来源于stack exchange,提问作者user3091275

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:18:03