Cythonのどツボり方シリーズ(4)

「気分」の問題。

Cython, pure Python の2つ組で一つの機能を構築しようとしてるとして。例えば「優しさ」を取得する KindDetector なんて機能全体を

  1. _kind_detector.pyx
  2. kind_detector.py

みたいな構成で作っているとする。パッケージはいま app.kind だとして。

例外を通知しようと思うわけである。して、_kind_detector.pyx にて例外を定義する:

1 # filename: app/kind/_kind_detector.pyx
2 class MissingKindHumanoid(EnvironmentError):
3     pass

そして kind_detector.py でこうする:

1 # filename: app/kind/kind_detector.py
2 import app.kind._kind_detector
3 from app.kind._kind_detector import MissingKindHumanoid

ビルドして動くことを確認する。よしよし。

ただ、思うわけだ、「app.kind.kind_detector が公開インターフェイスなのだから、_kind_detector.pyx ではなく kind_detector.py が MissingKindHumanoid を expose するのが筋じゃないのか」と。

して、このようにするわけだ:

1 # filename: app/kind/_kind_detector.pyx
2 from app.kind.kind_detector import MissingKindHumanoid

そして kind_detector.py でこうする:

1 # filename: app/kind/kind_detector.py
2 import app.kind._kind_detector
3 class MissingKindHumanoid(EnvironmentError):
4     pass

ビルド出来るので実行してみる:

1 me@host: kind$ (cd ../../ ; python kind_sample.py)
2 Traceback (most recent call last):
3    ...
4 ImportError: cannot import name MissingKindHumanoid

なんでだぁ! と、叫びがちだけれど。

うん、このパターン、Cython 関係なく pure Python どうしだって出来ませぬ。依存の循環だからね。

なんでこれが Cython ネタなのか、と言えば、最初の前置き:「Cython, pure Python の2つ組で一つの機能を構築しようとしてる」がね、Cython では結構多いパターンだからなの。なんだかんだ、「Cythonプログラミングは楽しくない」というか、不愉快なところも多いので、必要なければ pure Python 維持部分は多い方が良くて、必要最小限だけ Cython 側で、ということは良くやるわけで。そうすると両者の役割分担にてたまにこういう「FAQ だろそれ」と言うようなところでもハマることがある、と。

これの解は?

パッケージの「__init__.py」に書くことが解になるならそれが解と思いますよ。もはや「app/kind/kind_detector.py」というモジュールの公開物ではなく「app.kind」というパッケージの公開物になってはしまいますがね。たいがいそれでもいいんじゃねすけ?