最終更新:2026-10-12
CPython 拡張開発メモ¶
この記事では、最近 Apache に提出した feature を背景に、CPython で遭遇した問題を語り、さらに 0 から自分でライブラリを書く試みまで紹介します。コードも星球に置いてあるので、興味のある方はダウンロードして学んでみてください。
0.背景¶
最近、apache arrow に提出した feature で、C++ インターフェースの変更が他言語のインターフェース変更を引き起こし、一部のテストも合わせて修正が必要になりました。
PyArrow に詳しい人ならまったく馴染みがないわけではないでしょう。今回のインターフェース変更でもこのライブラリを修正する必要があります。同じリポジトリ内にあるため、直さないと CI が通りません。PyArrow の実装は CPython によるもので、これまで特に CPython を学んだことがなかったため、いろいろ問題を出してしまいました。そのうち 2 つの問題が比較的重要なので、ここに記録しておきます。
問題 1:初期化関数の中では他のクラスのデフォルト構築がサポートされない。
例:
エラー:
TypeError: descriptor '_scalar' for 'pyarrow._compute.Expression' objects doesn't apply to a 'bool' object
認識されなかったことが分かります。実際には Expression._scalar(True) は合法であり、中の実装を見てみます:
@staticmethod
def _scalar(value):
cdef:
Scalar scalar
if isinstance(value, Scalar):
scalar = value
else:
scalar = lib.scalar(value)
return Expression.wrap(CMakeScalarExpression(scalar.unwrap()))
中では通常の bool 型をサポートしていることが分かります。私はこれが cpython の制限ではないかと疑い、次のように変更したところ上手くいきました:
def __init__(self, mode="only_valid", filter=None):
if filter is None:
filter = Expression._scalar(True)
問題 2:定義の順序
後から作成した _true を使うと、毎回渡されるデフォルト値が空になります。これは理解しやすく、最終的にコンパイルが完了すると 1 つの xxx.cpp ファイルに翻訳されるため、C++ の規則により、前方で読み取られるものは当然空になるのです。
def __init__(self, mode="only_valid", filter=_true):
pass
cdef CExpression _true = CMakeScalarExpression(
<shared_ptr[CScalar]> make_shared[CBooleanScalar](True)
)
さて、以上の背景を踏まえて、私自身も 1 つ例を書いてみたくなりました。例えば:C++ でクラスを書いて sort と sum をカプセル化し、Python から呼び出すというものです。
1.CPython 完全な例¶
- .h ファイルを作成
void sort(std::vector<int>& nums) {
std::sort(nums.begin(), nums.end());
}
int sum(std::vector<int>& nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
return sum;
}
2) foo.pyx を作成
重要なポイント:上の vector には次が必要です:
次に class を定義して、C++ のインターフェースを呼び出します。
cdef class PyFoo:
cdef Foo* f
def __cinit__(self):
self.f = new Foo()
def __dealloc__(self):
del self.f
def sort(self, nums):
cdef vector[int] c_nums = nums
self.f.sort(c_nums)
def sum(self, nums):
cdef vector[int] c_nums = nums
return self.f.sum(c_nums)
- setup.py ファイルを作成
ext = Extension('Foo', sources=["foo.pyx"], language="c++", include_dirs=[numpy.get_include()])
setup(name="Foo", ext_modules = cythonize([ext]))
- 実行
最後に、テストスクリプトを書いて自作の python インターフェースを使ってみましょう。
import Foo
f = Foo.PyFoo()
nums = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
f.sort(nums)
print("Sorted nums:", nums)
print("Sum of nums:", f.sum(nums))
CPython はいくつかのプロジェクトでかなり使われています。学んでいきましょう〜
実行:
関連トピック¶
- C++ 文字列の 6 つの分割方法 | C++那些事
- libcurl C++ 開発メモ | C++那些事
- オープンソース C++ コードの読み方 | C++那些事
- ある面白い C++ コード | C++那些事
- C++ ループなしで 1-100 を出力 | C++那些事