PythonでWebアプリケーション作る練習をした

最近流行っている雰囲気があるPythonだけど、僕も年末にふと気分が高まったので、練習をしてみた。

自分にとってはPerlやRubyよりも先に勉強した、初めてのスクリプト言語がPythonだったので、ちょっとだけ思い入れがある。とはいえ、何年も前に初めてのPythonで勉強した後は、稀に使うくらいだったのでPythonならではの良い書き方とか、良いライブラリの知見とかは全然持ってなかった。

そこで、Pythonに入門しなおしてPython流を思い出した後、自分が気になっているWeb開発をやってみてどういうもんなのかを一通りやってみた。

このエントリは、GoでWebアプリケーション作る練習をしたの続編です(さらに続きはないでしょう)。

入門 Python3 を読んだ

入門 Python 3

入門 Python 3

一通りの文法を思い出そうということでとりあえず読んでみた。他の言語を一通りやっていれば新しい概念はほとんどないのでサクサク読める。僕は3,4日くらいでだいたい目を通せた。

ざっと読んだことで、Pythonならではのリスト内包表記とか、関数デコレータの話題とかを思い出せることが出来てよかった。PythonってリテラルにSet型があるんだなーとか。

Pythonの文法の話題だけにとどまらず、テストやデータベースへのアクセス、HTTPクライアントやサーバ(WSGI)の話題について一通り教えてくれるのも良い。最近の本らしく、NoSQL(Redis)やメッセージングキュー(ZeroMQ)あたりの話題も取り上げられていた。各章に練習問題がついてるので実際に手を動かすこともできる。

はてなインターンの事前課題

はてなインターン参加者は、はてなオフィスにやってくるまでの予習として、事前課題というのをやることになっている。ちょっと古いけど公開されていて、Perl版とScala版がある。その課題をPythonで解いた。ソースコードの python-Intern-Exercise はこちら

この事前課題は、プログラムの基本的な書き方、ファイルI/O、テストなどを網羅しているので、同じ内容の課題をPerlやScala以外の言語でやると良い練習になっておすすめ。ちょっと前に id:shiba_yu36 さんがやっていた、 Java版はこちら

Webアプリケーションの実装

さらに練習として、PythonでWebアプリケーションを作ってみることにした。あれこれ調べながら、自分がよくやるスタイルでWebアプリケーションを書くとどういう感じになるかを調べてみた。

github.com

結論としては、Pythonでどういう書き方をすると良いのかとか、どのライブラリを使うのかといったことを調べるのには時間がかかったものの、それさえわかれば、だいたいやりたいことはできるということがわかった。

普段はPerlを使っているのだけど、Perlで使っているテクニックやライブラリはだいたい対応するものがあって同じやり方が通用する感じ。例えば、テストの感じとかは、いつも書いているPerlのコードに近いインターフェースのサポート関数を利用できるようにできた。

もしかすると何かの参考になるかもしれないので、実装の方針を紹介しておいてみる。

PerlでWebアプリケーションを作るときにもよくやっている、薄いフレームワークの上になるべくシンプルにアプリケーションを構築するという方針でつくっている。複雑なしくみは使わず見通しの良さを重視してる。使っているライブラリは最低限で以下の様な感じ。

  • Webフレームワーク: Flask
  • データベース: ORMは使わずに mysqlclient をそのまま利用
  • テスト: nose (追記: 識者によると今開発止まってるのであんまり使わないほうがいいらしかった!)

ファイルツリーは以下のような感じになっていて、それぞれの役割も書いた。

.
├── db/
│   └── schema.sql
├── hakoblog/
│   ├── config.py  # アプリケーション設定用のクラス定義
│   ├── model/     # アプリケーションのモデル定義(BlogやEntryなど)
│   ├── loader/    # モデルオブジェクトをデータベースから取得するクラスが入ってる
│   ├── action/    # アプリケーションに対して破壊的な操作を行うクラスが入ってる
│   ├── db.py      # データベース接続を抽象化したクラスの定義
│   └── web.py     # Webアプリケーションのルーティングとコントローラ
│   ├── templates/ # HTMLテンプレート
└── tests

ORMは使わないので、modelの定義は単純なPythonのクラスになっている(例: Entryクラス)。

モデルオブジェクトをデータベースから取得するにはloader内で定義されたクラスを使う。クラスはただのネームスペースで、クラスメソッドとして処理を定義する。例えば、UserLoaderは以下のような雰囲気になる。

from hakoblog.model.user import User


class UserLoader():
    @classmethod
    def find_by_name(cls, db, name):
        with db.cursor() as cursor:
            cursor.execute(
                '''
                SELECT id, name
                FROM user
                WHERE name = %s
                LIMIT 1
                ''',
                (name, )
            )
            row = cursor.fetchone()

            if row is None:
                return None

        return User(**row)

SQL内に%sが出て来るのでドキッとするけど、これでちゃんとplaceholderになっている(mysqlclientの仕様で%sを使う)。

破壊的な操作をするときはactionに同様のクラスを定義することに、ざっくり責任をわけておく。こうすることでデータを読み込みつつ破壊的な操作を定義しにくくなる狙いがあったりする。

db.pyはDBクラスが定義してあってmysqlclientライブラリを薄くラップしてある。接続のための設定を読み込んだり、ちょっとしたユーティリティ関数(例: uuid_short関数)を定義するのに便利。

web.pyでは、HTTPのルーティングとコントローラを定義してある。アプリケーションのエントリポイントはここになる。

実装を試すのが主目的なので実用性はまったくない。記事書いたり記事を読んだりはできるけど、ユーザ認証などはないし、スタイルも書いてなくてみためは限界に近くしょぼい。

感想

ちょっとPythonに入門したあと、あれこれ調べながらPythonでWebアプリを0から書いてみた。

当然、普通にWebアプリケーションは書けるわけだけど、Perlでいつもよく使う設計スタイルも無理なく採用することができることがわかったのは良かった。コードを書いてみると、標語*1のとおりで、一つのことをやるやり方がだいたい決まっているので、迷わずにどんどん書き進められるのは心地よかった。

あんまり凝ったことをしない文化っぽいけど、必要になったらやっていくという感じがあるのもなんとなくわかってきた。例えば、テストのために時間をとめるライブラリ(freezegun)とかはちゃんとあったりして、柔軟性も大事にされていそう。

スクリプト言語としてなかなか良いバランスだし、Web開発がうまくできそうなことも体験できた。そうでなくても、機械学習やデータサイエンス分野で勢いのある言語だと思うので、もうちょっと練習して使えるようにしておいても良さそう。

*1:There should be one-- and preferably only one --obvious way to do it.