1. ホーム
  2. パイソン

[解決済み】C言語のライブラリをPythonでラッピングする。C、Cython、またはctypes?

2022-03-29 03:01:18

質問

PythonアプリケーションからC言語のライブラリを呼び出したいのですが、どうすればいいですか?API全体をラップするのではなく、私のケースに関連する関数とデータ型だけをラップしたいのです。私が思うに、私には3つの選択肢があります。

  1. C言語で実際の拡張モジュールを作成します。おそらくやりすぎで、拡張モジュールの書き方を学ぶためのオーバーヘッドも避けたいところです。
  2. 使用方法 Cython C ライブラリから Python に関連する部分を公開するためです。
  3. 全部をPythonで行うには、次のようにします。 ctypes を使用して外部ライブラリと通信します。

2)と3)のどちらが良いのか迷うところです。3)の利点は ctypes は標準ライブラリの一部であり、結果として得られるコードは純粋なPythonになります - ただし、この利点が実際にどの程度大きいかはわかりません。

どちらを選んでもメリット/デメリットがあるのでしょうか?どちらの方法をお勧めしますか?


編集する 回答ありがとうございました。もちろん、1つのケースに対しての判断はまだ必要で、「これが正しい」という答えはありません。私自身の場合は、おそらくctypesを使うことになると思いますが、他のプロジェクトでCythonを試してみるのも楽しみです。

FogleBirdの回答は、ctypesに関する良い洞察を提供しており、また現在最も投票数の多い回答であるため、私はFogleBirdの回答を選択しました。しかし、私は良い概要を得るためにすべての回答を読むことをお勧めします。

本当にありがとうございました。

解決方法は?

ctypes は早く終わらせるための最善の策であり、まだPythonを書き続けているあなたにとって、楽しく作業できるものです

私は最近 エフティーディーアイ ドライバは、ctypeを使用してUSBチップと通信するためのもので、素晴らしいものでした。 1日足らずですべて完成し、動作するようになりました。(必要な機能だけ、15個ほど実装しました)。

以前はサードパーティのモジュールを使っていました。 PyUSB を、同じ目的で使用することができます。 PyUSBは実際のC/Pythonの拡張モジュールです。 しかし、PyUSBはブロッキングリード/ライトを行うときにGILを解放しないので、私たちに問題を引き起こしていました。 そこで、私はctypesを使用して独自のモジュールを書きました。

注意点としては、types は #define の定数などは、使用するライブラリの関数だけなので、自分のコードでそれらの定数を再定義する必要があります。

以下は、最終的にどのようなコードになるかの例です(たくさんの部分を切り取って、要点を示したいだけです)。

from ctypes import *

d2xx = WinDLL('ftd2xx')

OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3

...

def openEx(serial):
    serial = create_string_buffer(serial)
    handle = c_int()
    if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
        return Handle(handle.value)
    raise D2XXException

class Handle(object):
    def __init__(self, handle):
        self.handle = handle
    ...
    def read(self, bytes):
        buffer = create_string_buffer(bytes)
        count = c_int()
        if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
            return buffer.raw[:count.value]
        raise D2XXException
    def write(self, data):
        buffer = create_string_buffer(data)
        count = c_int()
        bytes = len(data)
        if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
            return count.value
        raise D2XXException

誰かがやった いくつかのベンチマーク 様々なオプションについて。

C++のライブラリをたくさんのクラス/テンプレート/その他で包まなければならない場合は、もっと躊躇するかもしれません。 しかし、ctypesは構造体をうまく扱うことができ、さらに コールバック をPythonに取り込むことができます。