うっかりやってしまって、あまりの凡ミスぶりに自分でも赤面したものです。
何が失敗だったのか?
djangoで学習済のWord2Vecモデルを使った画面の整理をしました。
使い捨てのつもりで、モデル生成もviews.pyに書いていたのをモジュール化して、UnitTestも書いておこう。
そう思ったわけです。
自分が最近使っているフォルダ構成はこんな感じ。
functionとかmoduleを階層化して、階層をとばして結合しない縛りをいれてます。
なので。
modulesフォルダに本体である「word2vec.py」を置き、word2Vecモデルを使った何等かの処理クラスはfunctionsフォルダにおき、views.pyではfunnctionsフォルダのクラスだけを使うようにするわけです。
word2vec.py内部で使う学習済モデルファイル(日本語の場合はja.bin)のパス(今回はjaフォルダ)は、動的に引数で与える想定なので、jaフォルダのパスは使う側のfunctionsのソースおよびTestCase内に書きます。
お試しの時は、「ja.bin」は、views.pyのあるフォルダ直下に「ja」フォルダをきって、そこにおいてましたので、ja.binの絶対パスは
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
みたいにsettings.pyのBASE_DIRの記述からコピペし手書いていました。
これを、functionやunittestのソースににうつすときに、他のソースごとコピーしてしまった。
これが今回の大失敗です。
どのあたりが凡ミスなのか
フォルダ階層が違うソースコードに、「os.path.abspath(__file__)」なんて書いて、同じパスが返されるわけがありません。
note.nkmk.mesettings.pyの「BASE_DIR」の記述は以下です。
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIRとは「settings.py」のあるフォルダ・・ですから、当然です。
お試しの時は、settings.pyと同じ階層にあるviews.pyに書いていたので、その部分のコピペで、「os.path.dirname(os.path.dirname(os.path.abspath(__file__)))」と書いても同じパスが保証されるので、手抜きしてコピペしてました。
ところが。
そんなことをすっかり忘れて、ソース丸ごと、コピペしてしまった。
これが問題です。
人は忘れる生き物なのだから、そういう環境依存のことをしてる時は、コメントを書いておけよ・・ということですね。
反省です。
どうすればよかったのか。
djangoには「settings.py」で定義された値を読む機能が用意されています。
それを使えばよいだけです。
例えば、BASE_DIRを使いたいなら。
from django.conf import settings
basedir = getattr(settings, 'BASE_DIR', None)
ですね。
このやり方なら、views.pyに書こうが、下の階層のモジュールやテストソース内に書こうが、安定して同じ「BASE_DIR」を取得できます。
だから。
本当なら「ja.bin」のパスも、settings.pyの中に「JA_BIN_PATH」のように独自の名前で定義して、それを読み出せばいいだけです。
例えば、settings.pyで。
JA_BIN = '\\etcdemo\\functions\\modules\\ja\\ja.bin'
のように独自定義を追加しておいて。
self.basedir = getattr(settings, 'BASE_DIR', None)
self.jabin = getattr(settings, 'JA_BIN', None)
self.word2vec = wv.Word2Vec(self.basedir + self.jabin)
のように、ja.binファイルのパスを取得します。
まとめ
凡ミスで余計な時間を使いました。
でも、ひとつだけよいことがありました。
それは、そのミスに気づけたことです。
最近、このエッセイを読み直し
xn--97-273ae6a4irb6e2hsoiozc2g4b8082p.com
この中の
美しいコードとは、突き詰めれば、シンプルなコードのことです。
システムを構成する各部分が全てシンプルで、個々の部分が担う責務も最小限に抑えられていて、部分どうしの関連もシンプル、そんなコードです
に共感して、シンプル化と自動テストをするように書き直してます。
その一環で作業をしていたのですが。
以前のように、単純にword2vecに関する処理本体だけをモジュール化して、views.pyの中でja.binのパスを指定するような中途半端な整理の仕方だと、このミスは顕在化せず、潜在バグとして隠れていたことになります。
そこを「階層をとばした結合をもたない」ために、きちんと分けたから気づけた・・ということなので、それなりに意味があったと思っています。
やっぱ、基本は大事です。
ではでは。