11

別のオブジェクトの属性としてタプルを作成し、タプルに項目を設定する Python 拡張モジュールがあります。このモジュールを Python で実行すると、常にエラーが発生します。SystemError: bad argument to internal function

のドキュメントを読みPyTuple、プログラムを数時間デバッグした後でも、一体何が起こっているのか理解できませんでした。デバッガーを介してプログラムを実行すると、Python インタープリター内のライブラリー呼び出し内で問題が発生していることが示されました。というわけで、ようやくPythonのソースコードを見て、ようやく問題に気づきました。関数には、PyTuple_SetItem私が知らなかった興味深い制限があり、明示的に文書化されていません。

Python ソースの重要な関数を次に示します (わかりやすくするために編集しています)。

int PyTuple_SetItem(register PyObject *op, register Py_ssize_t i, PyObject *newitem)
{
    .....
    if (!PyTuple_Check(op) || op->ob_refcnt != 1) {
        Py_XDECREF(newitem);
        PyErr_BadInternalCall();
        return -1;
    }
    .....
}

ここで重要な行は条件op->ob_refcnt != 1です。ここに問題があります: Tuple の ref-count が 1 でない限り、呼び出すことさえできませPyTuple_SetItemん。結局のところ、タプルは不変であるはずなので、これは理にかなっていると思います。したがって、この制限は、C コードを Python 型システムの抽象化とより一致させるのに役立ちます。 PyTuple_SetItemPyTuple_New()

ただし、この制限はどこにも文書化されていません。関連するドキュメントはherehereにあるようですが、どちらもこの制限を指定していません。ドキュメントでは基本的に、 を呼び出すとPyTuple_New(X)、タプル内のすべての項目が に初期化されると書かれていますNULL。は有効な Python 値ではないためNULL、Tuple をインタープリターに返す前に、Tuple 内のすべてのスロットに適切な Python 値が入力されていることを確認するのは、拡張モジュール プログラマの責任です。しかし、Tuple オブジェクトの参照カウントが 1 のときにこれを行う必要があるとはどこにも述べていません。

さて、問題は、この (文書化されていない?) 制限を認識していなかったため、基本的に自分自身をコーナーにコーディングしたことですPyTuple_SetItem。私のコードは、タプル自体が別のオブジェクトの属性になるまで項目をタプルに挿入するのが非常に不便なように構造化されています。そのため、タプルにアイテムを入力するときが来ると、タプルはすでにより高い参照カウントを持っています。

おそらくコードを再構築する必要がありますが、Tuple の参照カウントを一時的に 1 に設定し、アイテムを挿入してから、元の参照カウントを復元することを真剣に検討していました。もちろん、これは恐ろしいハックであり、恒久的な解決策ではありません。とにかく、タプルの参照カウントに関する要件がどこかに文書化されているかどうかを知りたいです。それは単なる CPython の実装の詳細ですか、それとも API ユーザーが期待される動作として信頼できるものですか?

4

2 に答える 2

8

PyTuple_SET_ITEMの代わりにを使用することで、制限を回避できると確信していますPyTuple_SetItem。次のようPyTuple_SET_ITEMに定義されたマクロです。tupleobject.h

#define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject*)(op))->ob_item[i] = v

だから、もしあなたが絶対に、間違いなくそして完全にそれを確信しているなら:

  1. opタプルオブジェクトです
  2. iこれまでにタプルのスロットを初期化していません
  3. あなたはへの参照を所有していてv、タプルにそれを盗ませたいと思っています
  4. 呼び出す前に、別のPythonオブジェクトがタプルを何かに使用する可能性はありませんPyTuple_SET_ITEM

その後、私はあなたが安全に使用できると思いますPyTuple_SET_ITEM

于 2011-05-24T14:20:39.110 に答える
2

Python C APIは非常に文書化されていないため、この制限がどこにも言及されていなくても驚かないでしょう。

もちろん、何かがタプルを取得した後は、タプルを変更しないでください。タプルに入れる必要のある要素を渡すか、代わりにリストを使用してください。

于 2011-05-24T14:19:56.253 に答える