こんにちは、Pythonエンジニア育成推進協会 顧問理事の寺田学です。私は試験の問題策定とコミュニティ連携を行う立場です。
Pythonを学ぶ際、機能ごとに段階を追って学び、徐々に一つずつの機能を作る方法を習得していくことがあるかと思います。最終的に、そうして作ってきた機能をひとつのアプリケーションという形に落とし込むということになりますが、その一つのアプリケーションにするという段階に壁を感じている人は多いと思います。
そこで今回は、機能分割についてお話したいと思います。
■アプリケーションは機能の集合体
初心者はアプリケーションを作るとき、まずは個別の機能のつくり方を学び、できた機能を、流れるようにつなげていきます。
例えば、ランダムに生成された数字を当てるというアプリケーションを作りたいとなった場合、
- ランダムな数字を作る機能
- 画面から数字を入力する機能
- 当たり判定をする機能
- Tkinterなどを使って画面に出力する機能
などの複数の機能が必要になりますが、これらを最終的にすべてつなげると、「数字当てゲーム」という一つのアプリケーションになります。ただ、作った各機能をひとつにまとめるのは、初心者にとって少しの難しさがあるように思います。
ここでもう少し「機能」について掘り下げてみます。
例えば、機械学習で、Jupyter LabやGoogle Colaboratoryなどの機能を使うと、機械学習モデルやLLM(大規模言語モデル)を活用した自動返信チャットのようなものも作ることが出来ますが、これもひとつの機能が作れたと言えます。
ただ、作ったこの機能を、多くの人に使ってもらえるようにしたり、より使い勝手のいいものにしたりするには、アプリケーションの中に取り込むことになりますが、どうアプリケーションに取り込めばいいのか分からず、ここでもひとつのハードルと感じている人は多いようです。
■処理の流れときっかけを意識する
さて、複数の関数や機能を上手く組み合わせれば、一連の流れが作られた1つのアプリケーション(ツール)として機能するものを作れたと言えます。
アプリケーション作りにおいて重要なことは、 何かをきっかけに、何らかの処理が順に処理されていきますし、きっかけがなければ処理は進みませんので、アプリケーション作りではそれを意識する必要があるということです。
(この世界には非同期などの、すんなりとはいかない難しい要素もありますが、最初は一旦忘れましょう。)
例えば、先ほどの数字あてゲームの例で言えば、画面に数字が入力されたことをきっかけとして、ランダムに生成された数字と同じかを判定し、その結果を画面に出力するというように、順番に処理が完了します。
「きっかけ」の実装は、多くの場合は関数やクラスメソッドなどのいろんなパターンで作られていますが、組み合わせて順番に呼び出す、または呼び出しの中で連携して組み合わせて作られ、最後に出力という形で結果が出されます。
例えばWeb表示、アラート、画面の色を変えるなどの変更、ファイルへの書き出し、データベースへの処理、ログへの排出などの他に、何もしないなどが処理のゴールとして設定されます。
では、いざアプリケーションを作り上げようとするときに問題にはなるのは何かといえば、この「きっかけ」と、「どのように中身を組み立てるか」で、どちらもとても重要な要素です。
■「きっかけ」となる部分を作る上で知っておきたいこと
Webの場合、Webの仕組みの多くを理解していることが必要です。
ただ、昨今のWebはJavaScriptやTypeScriptでできているフロントエンドが多くなっていますが、Webフレームワークがいろいろな処理をしてくれるため、サーバーサイド側をPythonで見た場合に、何が、いつやってきて、どうなったのか、が非常にわかりにくくなっています。
これはデスクトップアプリでも同じです。デスクトップアプリは、何かが入力されるまで待ち受け画面のまま待機しますが、この入力待ちから、アプリケーションの機能を実行するまでに至るキックを設定する手法が必要になります。
Webにしても、デスクトップアプリにしても、処理をスタートさせる機能などのきっかけが来るのを待つという処理がないとある一定レベルの完成したアプリケーションにはなりませんので、これが難しさを感じさせる要因となっているのではないかと思います。
1番簡単なアプリケーションのきっかけ、コマンドライン上で、作ったアプリケーションを実行することです。
コマンドライン上でスクリプトを実行したら、アプリの中の処理が順番に実行され、結果を出力するというものですが、この場合、スタート地点を自分で決めて、自分でスタートさせているので、スタートがどこなのか、関数をどのように実行し、結果をどう出力するのかを考えればいいだけです。
この考え方をWebに応用すると、Webはリクエストが来たときに、リクエストを受け取るハンドラーという関数やクラスなどがあります。それによって、URLが入力されて呼び出された時に実行される関数が決まっていますので、コマンドラインツールのように、実行されたときに起動する関数と同じような処理を書いておくことで処理を進めることが出来ます。
また、Webは複数のURL(エンドポイント)が存在するため、複数のキックポイント(スタート地点)が作られます。このスタートに基づいて、最終的にデータベースやファイルに書き込むといった操作を関数やクラスの中で実行されます。
もう少しWebの話をすると、Webは最終的にHTMLを出力する、APIサーバーにJSONを返すなどの出力結果を外に出していくことになりますが、これらもDjangoテンプレートやJinja2などのテンプレートや、JSONのフォーマットに出力する機能などを持つWebフレームワークがありますので、そのフレームワークに従って出力することになります。
一方、デスクトップアプリの場合は、入力されたり、クリックされたりするまでは、きっかけとなるものがありませんので常に待ち受けをしている形になっています。
デスクトップツールを作る場合、多くの場合でフレームワークを使いますが、ほぼフレームワークが待ち受けを担ってくれます。待ち受けの状況に対して、どうなると変更され、処理が呼び出されるのかはフレームワークごとに違うため、使用するフレームワークの指示に従って、変化をとらえるためのメソッドやどう処理をするかという関数を書いておくと、それが自動的に呼び出されるようになります。
いずれにせよ、何かしらの手段で、「起動のきっかけ」をとらえて「処理(出力)する」という関数やメソッドを書く必要があります。
フレームワークまたはコマンドラインツールは多くの場合、じかにモジュールを書いて、そのモジュールを実行すればいいだけなので、関数かクラスのメソッドで、何かしらの処理を書けば問題ありません。
ただ、最初は関数をひとつのファイルにまとめるところから始めるのが簡単だと思います。
■「中身を作る」ときに知っておきたいこと、機能分割
例えば、数字あてゲームで言えば、ランダムな数字の出力をrandomモジュールで出せますし、ユーザーが入力した値とチェックする部分は、if文を使って判定させられますので、1つの関数にまとめて処理を終わらせることが出来ます。
一方、機械学習のモデルから何かの出力を求めるというような、より複雑な処理が必要な場合には、1つ当たりの関数がとても長くなってしまい、メンテナンス性が悪くなりますし、使いまわしもしにくくなります。だからこそ、関数を分割して、機能別に作っておくという考えが出てきます。
例えば数字あてゲームで言えば、ランダムな数字を出すという関数、当たり判定をする関数、結果を出力する関数というように、機能別に関数を作成し、モジュールという形で切り出します。作成した関数に、引数と戻り値があるという形にすれば、それをきっかけとして次の機能を呼び出すことが出来ますので、順番に次の関数が呼び出され、ひとつのアプリケーションとして成り立たせることが出来ます。
では関数がどこにあればいいのか。これは色々なパターンが想定されます。
Pythonでは、「.pyファイル」という1つのファイルに書かれているファイルをモジュールと言いますが、このモジュールの中で、必要な機能の関数を続けて書くというのがまず一つです。
次に、作ったモジュールを使いまわししやすいように、個別のファイルにしておいて、ひとつのファイルで分けたモジュールを個別にimport文を使って呼び出すという方法です。
さらに、より汎用的なものになるものであれば、パッケージ化して、別パッケージとして呼び出すということもできます。ただ、この場合、使いまわししやすい一方で、Pythonのパッケージングの手法を学ばなければできませんので、初心者ではなかなか難しいと思います。
ちなみにサードパーティ製パッケージはこうしたパッケージ化によるもので、例えばpandasなどがそれにあたりますが、そうしたパッケージはPyPIで公開されており、インストールして、import文を使えば、誰でも使うことが出来ます。
パッケージの中に入っている機能がどのようなものであるかを知っていれば、呼び出される関数の中で、上手くその機能を使って何かしらの変数に代入することで、次の関数にわたせるようになり、関数やクラスオブジェクトを積み重ねて機能にしていくことが出来ます。
ここでポイントとなるのが引数と戻り値です。適切な引数を与えて、適切な戻り値を返してもらわないと上手くいかないため、最初にしっかりと設計を考えておきましょう。
■さいごに
さて、とにもかくにも、アプリケーションとしてただ成り立たせるだけなら、ひとつのファイルに必要な機能の関数をすべてかくだけでもかまいませんし、むしろ初心者はそこから始めるのが良いと思います。
ただ、ひとつのファイルに長々と関数が書かれている状態は、メンテナンス性が良いとは言えませんし、外から見たときに非常に分かりにくくなります。そのため、応用の利く機能なのであれば、分割しておくと後々、別のアプリケーションに流用できるので、慣れてきたら機能分割を少しずつやっていけばいいと思います。
アプリケーション作りは初心者にとってとても難しく、遠く感じるものだと思います。ですが、何かを作るという目標がなければ、なかなか学習のモチベーションを維持することは難しいと思います。
Pythonには便利なフレームワークやモジュール、ライブラリがたくさんありますので、それらを上手く使って試しつつ、良い書き方を覚えていきましょう。
とはいえ、フレームワークは上手く使えればとても便利ですが、フレームワーク自体の流儀があります。中には機能の分割を要求するものもありますので、やりたいことに合わないのであれば無理に使わず、別の手段を取りましょう。気に入ったフレームワークを見つけて、まずは一つを極めてみるのもいいかもしれません。
アプリケーションをひとつ作れること。それだけでも十分にすごいことです。機能を上手くつなげられるのなら、ひとつのファイルに関数をつなげていく方法でも、機能分割する方法でも、どちらの手段をとっても問題ないと思いますので、いろいろチャレンジしてもらえればと思います。