速度を上げるために Python プログラムに C 拡張機能を書いていますが、3 次元の numpy 配列を渡そうとすると非常に奇妙な動作が発生します。2次元配列で動作しますが、3次元で動作させようとしているポインターで何かを台無しにしていると確信しています。しかし、ここに奇妙な部分があります。3 次元配列だけを渡すと、Bus Errorでクラッシュします。(Python で) 最初に変数を 2D 配列として作成し、次に 3D 配列で上書きすると、完全に機能します。変数が最初に空の配列で、次に 3D 配列である場合、Seg Faultでクラッシュします。どうしてそれが起こり得るのでしょうか?
また、3D 配列を機能させるのを手伝ってくれる人はいますか? それとも、あきらめて 2D 配列を渡し、自分で形状を変更する必要がありますか?
これが私のCコードです:
static PyObject* func(PyObject* self, PyObject* args) {
PyObject *list2_obj;
PyObject *list3_obj;
if (!PyArg_ParseTuple(args, "OO", &list2_obj, &list3_obj))
return NULL;
double **list2;
double ***list3;
//Create C arrays from numpy objects:
int typenum = NPY_DOUBLE;
PyArray_Descr *descr;
descr = PyArray_DescrFromType(typenum);
npy_intp dims[3];
if (PyArray_AsCArray(&list2_obj, (void **)&list2, dims, 2, descr) < 0 || PyArray_AsCArray(&list3_obj, (void ***)&list3, dims, 3, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "error converting to c array");
return NULL;
}
printf("2D: %f, 3D: %f.\n", list2[3][1], list3[1][0][2]);
}
上記の関数を呼び出す Python コードは次のとおりです。
import cmod, numpy
l2 = numpy.array([[1.0,2.0,3.0], [4.0,5.0,6.0], [7.0,8.0,9.0], [3.0, 5.0, 0.0]])
l3 = numpy.array([[2,7, 1], [6, 3, 9], [1, 10, 13], [4, 2, 6]]) # Line A
l3 = numpy.array([]) # Line B
l3 = numpy.array([[[2,7, 1, 11], [6, 3, 9, 12]],
[[1, 10, 13, 15], [4, 2, 6, 2]]])
cmod.func(l2, l3)
したがって、A 行と B 行の両方をコメントアウトすると、バス エラーでクラッシュします。行 A が存在し、行 B がコメントアウトされている場合、行 B はエラーなしで正しく実行されます。ライン B はあるが、ライン A がコメントアウトされている場合、正しい番号が出力されますが、Seg fault が表示されます。最後に、両方の行が存在する場合は、正しい番号と Seg faults も出力します。ここで一体何が起こっているのですか?
編集:わかりました。わお。だから私はint
Pythonで使用していましたがdouble
、Cでそれらを呼び出していました.そして、それは1Dおよび2D配列でうまく機能していました. しかし、3D ではありません。そこで、l3 の Python 定義を float に変更したところ、すべてが素晴らしく機能するようになりました (Bi Rico に感謝します)。
しかし、今、ライン A & B でより奇妙な動作! 両方の行がコメントアウトされている場合、プログラムは機能します。行 B が存在するが、A がコメントアウトされている場合、それは機能し、両方がコメント解除されている場合も同様です。しかし、ライン A が存在し、ライン B がコメントアウトされている場合、素晴らしいバス エラーが再び発生します。私は将来これらを避けたいと思っています.Python変数の宣言がこの種の影響を与える理由を誰かが知っていますか?
編集 2:まあ、これらのエラーと同じくらい正気ではありませんが、それらはすべて、私が渡す 3 次元の numpy 配列によるものです。1 次元または 2 次元の配列のみを渡すと、期待どおりに動作し、他の Python 変数は何もしません。このことから、問題は Python の参照カウントのどこかにあると思われます。C コードでは、参照カウントが 3 次元配列に対して必要以上に減少し、その関数が返されると、Python はオブジェクトをクリーンアップしようとし、NULL ポインターを削除しようとします。これはあくまで私の推測であり、考えられるPy_INCREF();
限りのことを試してみましたが、うまくいきませんでした。2D 配列を使用して C で再形成するだけだと思います。