7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Streamlit 導入一日目から爆速開発をするための(オレオレ)デザインパターン

Last updated at Posted at 2023-07-01

Streamlitにべた惚れ中です。

昔書いたShinyアプリを導入一日目で移植できて感動したものの、独自のクセに気づくのに3日ほどかかってしまったので、ちょっと複雑なことをやろうとする前にご覧いただければ幸いです。

結論

  • 「ここだけ更新されれば効率的なのに」みたいなことは気にせず、「ああ、毎回実行されるのね」という気分でつらつら書くべし
  • 決まったことをst.session_stateに追加するべし!
  • st.stop()を効果的に使うべし!!
  • レイアウトはMarkdownレベルだと思うべし!!!
  • streamlitに組み込む前のユニット関数の実験はJupyterNotebook形式でやって残しておくと便利!

「ここだけ更新されれば効率的なのに」みたいなことは気にせず、「ああ、毎回実行されるのね」という気分でつらつら書くべし

Event/Callback/Reactivityみたいな概念がそもそも無いです。うまくクラスとかモジュールにまとめたり、MVCだとかMVPだとかMVVMだとかをしたくなっても我慢して、ひたすらスクリプトに沿って実行されることだけ考えましょう。

なので、レイアウトの記述順というのが重要になります。普通のフレームワークだとUIとイベント管理は独立しているのであまり気にする必要が無いのですが、どの順番で書くかを気にする必要があります。

例えばサイドバーにデータの取得条件を選択するウィジェットを配置しメインにその結果を表示する場合は、先にサイドバーを記述すると不要なifを削減することができます。記述順が入れ子になる場合はst.container()などを使用し、一旦場所を確保しましょう。

決まったことをst.session_stateに追加するべし!

APIなどにログインが必要な場合、ログイン画面→データ表示画面への遷移をしたいことがあるかと思います。この場合には以下のような対応をします。

if login not in st.session_state:
    #ログイン画面
    ...
    #ログインできたら
    st.session_state.login = "done" #何かをセットすれば動きます
    ...
else:
    #ログイン完了後の処理
    ...

st.stop()を効果的に使うべし!!

さて、ちょいちょいやりたいことを追加していくと、ログインできていないとか、データフレームがまだ作られていないとか、値が定義されていないとかのチェックなどで自然とこんなイメージのコードになっていったりします。

if ログインできたら:
  ...
  with ... :
    ...
    if データが格納されたら:
      ...
      with ...:
        ...
    else ... :

もちろんこれで動作するのですが、インデントが深くなっていくというのは精神衛生上良くないですよね。そういう時は st.stop() で処理をぶった斬ると良いです。 st.stop() は以後の処理を行わない、というメソッドです。うまく利用できるとこんな感じにインデントを浅くすることができます。

if 'login' not in st.session_state:
    st.stop()

with ... :
    if 'df' not in st.session_state:
        st.warning('Please fetch the data')
        st.stop()
    with ...:

表示が真っ白でユーザーが心配になりそうな場合は st.warning() などで知らせてあげると親切ですね!

レイアウトはMarkdownレベルだと思うべし!!!

ネットの記事で「細かいレイアウトはつらい」という記述をよく見たのですが、心の奥底で「でも何とかなるでしょ?」と思ってしまうものです。もちろん力技で何とかすることはできるのですが、streamlitの強さを最大限活かすには、可能な限り最小限の記述で済ませましょう。コツは「レイアウトしていく」というより、 Markdownを記述している気分 で書いていくと良いです。

streamlitに組み込む前のユニット関数の実験はJupyterNotebook形式でやって残しておくと便利!

これも一週間ぐらいいろんな手法を試してこのやり方に行きつきました。
Streamlitはホットリロード(サーバーを立ち上げ直さなくてもコードが適用)されるので、演算や表示を変更したら画面へリアルタイムで反映され確認できます。なのでアルゴリズム実験をこの方法でできて便利だなーと思って最初はこれでやっていたのですが、後日やっぱりこの方法だと推敲の軌跡にアクセスできなく、Streamlitのコードのコメントをたどったりデータをもう一度喰らわせたりする必要が出てきてしまうなと気づきました。

なので自分は別フォルダにユニットテスト用のJupyterNotebook形式(.ipynb)を配置し、同じモジュール読み込んで結果を残しておく方式にしました。やっぱりJupyterNotebook形式は後で考えを見返すには最適ですよね。VSCodeだと.py/.ipynbをいったりきたりしやすいのと、CSVに吐き出して軽量なRainbow CSV extensionでリアルタイムにデータの反映を確認することで、ここらへんの効率・品質をかなり上げることができるようになりました。

Screenshot 2023-07-07 10.49.16.png

結論(再掲)

ということで、結論の再掲です。皆さんもぜひ爆速開発楽しんでください!

  • 「ここだけ更新されれば効率的なのに」みたいなことは気にせず、「ああ、毎回実行されるのね」という気分でつらつら書くべし
  • 決まったことをst.session_stateに追加するべし!
  • st.stop()を効果的に使うべし!!
  • レイアウトはMarkdownレベルだと思うべし!!!
  • streamlitに組み込む前のユニット関数の実験はJupyterNotebook形式でやって残しておくと便利!

streamlitを使い始めた背景

おまけのお話です。

仕事で使う分析環境を整理していて、まずはIDEをVSCodeに統一しました。ちなみにjupyterもこのタイミングでjupyterlabからVSCodeに乗り換えました。

そろそろRを引退させたかったのですが、Shinyを何とかしなくてはいけないところが足を引っ張っていました。

試しにShiny for PythonでUIだけ移植を試みたものの、、

Pythonではdashのほうがこなれているからやっぱりdashにするか〜と思い始めていて、良い評判だけ聞いていたstreamlitを試しに使ってみたところ、、

結局その日みっちり作業してみたらロジック部分まで移植が完了。

で、実はやりたいことは毎度秒速で実装できていたのですが、一番時間を使ったのがデザインパターンみたいなところでした。デザインパターンの記事を見なかったので書いてみた次第です。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?