概要
ここ数ヶ月、業務でPythonを多用してきました。使用内容としては、
- 別のツール(Apache Airflowなど)で使用されている言語(≒DSL)なので、仕方なく使用
- 計測データを読み込んで加工し出力する、データ加工用スクリプト言語として使用
- WebフレームワークFlaskで、Webアプリケーションを作成するための言語として使用
といったところです。その間、軽量言語であるPythonの利点を享受していましたが、同時に動的言語であるPythonの面倒さも味わいました。その詳細をざっとまとめたのが当記事になります。
利点
・Pandasがあまりにも強力
業務内では、計測データがCSVやExcelシートやparquetやデータベースなど、様々な形式で保存されていました。結局データベースに集約させるわけですが、そこまでに
- 様々な形式のファイルを読み込み、様々な形式として保存する
- 要らないデータ(NaNなど欠けている箇所がある・データの書式がおかしいなど)を削除する
- 必要な行・列を条件を指定して抽出する
- データを集合演算や公開関数で一括処理する
といった操作が必要でした。それらにPandasがジャストミートし、Pandas経由で間接的に使用することになるNumPyと共に活躍しました。
・PyCharmが非常に使いやすい
社内ではMac OS上で開発していましたが、その際メインで使用していたIDEがPyCharmです。無償版も商用利用できますし、Alt+Enterからの自動修正機能(必要なモジュールのimport文追加・importの整理・自動フォーマットなど)が強力無比でした。
後述する「sys.pathを書き換えてimport」に表示部分が対応していない……といった欠点もありますが、少なくとも単なるテキストエディタで書くより遥かに捗るのは事実です。
・インタラクティブモードで文法確認が捗る
Pythonはスクリプト言語ですので、実行環境による事前の構文チェックが効きません。明らかにおかしいものはIDEがチェックしてくれ(ることもあり)ますが、
- この構文は正しかったっけ?
- この変数にこのメソッドは呼び出せたっけ?
- そもそもこの変数の型は何?
といった疑問が定期的に湧いてきます。その際、インタラクティブモード上でスクリプトをコピペして実行することで、任意の変数の型を調べたり、構文やメソッドの動作チェック(※例外が吐かれてもその場で実行環境が止まったりしない)をすることができます。
特に配列などのスライスは直感的でないことから、インタラクティブ環境でチェックしまくれるのは非常に助かりました。
・豊富なライブラリをすぐに使用できる
pipコマンドから様々なライブラリをインストールできるので、「あのライブラリ欲しいなー」となった際は即座に利用できるのは便利でした。Pandas以外にもNumPyやmatplotlibやScipyなどの強力なライブラリ群が使えるのはPythonの大きな強みと言えます。
また、ローカル環境のPythonにそのまま放り込むだけでなく、PyCharm・pyenvを経由して特定のプロジェクトにだけインストールといったこともできるのが非常に捗りました。ローカルにインストールしているPythonと、クラウド上で動作するPythonのバージョンが違うのはよくあることでしたので……。
・インデントで構文を表現するとか天才かよ
括弧を多用せずとも処理構造が表現できるのは驚きでした。……逆に言えば「不用意にインデントできない」ということにもなりますが。
欠点
・メソッドチェーンしたいだけの人生だった
もちろんPythonでもメソッドチェーンは実装できますし、Pandasの各種関数はメソッドチェーン対応なのでそこはいいのです。
ですが、Pythonデフォルトのmapやfilterがメソッドチェーン対応ではない上、listの各種メソッドに破壊的なものが多いため、複雑な処理を施そうとすると1行の文字数か単文の行数が嵩む問題が発生します。
……ここでTwitter上で「リスト内包表記を使うべきでは(要約)」といったツッコミが返ってきました。確かに使ったことがないので調べてみましたが、mapとfilterの仕事をこなせるので慣れれば強力な気がします。
・型が指定できない災厄が降りかかる
Pythonは動的言語ですので、変数の型が実行時まで決定しません。いわゆるダックタイピングなのですが、静的言語と違ってinterfaceを介しませんので、本当にどんな型が降ってきても受け入れる必要があります。
同じ動的言語でも、PHP(5.4~7以降)やTypeScriptの場合、変数宣言やメソッドの引数や戻り値の型を制約すれば、それが実行時に自動でチェックされたり、IDE上で型のサポート(メソッドの自動補完など)を受けられるといったことができます。
しかし、Python3.6以降に搭載されたタイプヒンティングは実行環境がどうこうするわけではなく、またライブラリがタイプヒンティング前提に組まれていないため、メソッドの戻り値に型が指定されていない→明示的なタイプヒンティングを繰り返す必要があるといった面倒臭さがあります。
もっとも、そこを改善しようとしますと、TypeScriptのように型宣言情報をいちいち揃える必要がありますが……。
・変数スコープの仕様が人類には辛すぎる
これについては他の方の記事「Pythonの変数スコープの話」を参照してください。とにかくグローバル汚染されまくるので、いくらIDEで「その変数名だと汚染されるから気をつけてね!(意訳)」と表示されたとしても、感謝すると言うより「なんでそんな人類に優しくない仕様になっているのか」感が……。
(※上記画像は、業務で使用しているコードでは勿論ありません。「if name == 'main' の下にコードをダラダラと書く人、挙手しなさい」も参照しましょう)
・お前のimport仕様はおかしい
開発中にModuleNotFoundErrorを何度見たことか。
……相対パスの能力に限界があるのでsys.pathの書き換えが必要なのは本当にキツいのですがそれは。Javaのように「ディレクトリ構造と呼び出し方(≒名前空間)が一対一対応」する仕様で、C#やPHPのように名前空間を好きに設定できるわけでないのがボディーブローのように効いてきます。
・その他枝葉末節
- Python標準のdictが「
for key in dict
」形しかない。「for key, val in dict
」も欲しかった- これについては「
for key, val in dict.items()
」でいいといった意見をいただきました - ただ、「キー→(無印)、値→values()、両方→items()」なので、「キー→keys()、値→values()、両方→(無印)」の方が分かりやすかったなといった印象です(Rubyがそんな感じ)
- これについては「
- なんで条件演算子が「
x = (y == 1) ? 2 : 3
」じゃなくて「x = 2 if y == 1 else 3
」とトリッキーな構文になっているの? - なんでdocstringの書式が複数通りあるの???
- CSSセレクタの実装がクソなBeautifulSoupに[煮え湯を飲まされた]
(https://twitter.com/YSRKEN/status/1057648760883339267)。[lxml](https://lxml.de)の方が断然速いし正しく動作するやんけ!