本当に誰でも読める!! Pythonソースコードの読み方講座(第2回) classにまつわるコードの読み方

  • HOME
  • 本当に誰でも読める!! Pythonソースコードの読み方講座(第2回) classにまつわるコードの読み方

本記事は当社が発行しているシミュレーションメールマガジンVol2.の記事です。

シミュレーションメールマガジンの詳細・購読申込はこちら

本当に誰でも読める!! Pythonのclassにまつわるコードの読み方

S4 Simulation SystemはGUI操作でシミュレーションモデルを構築できることが魅力ですが、ユーザ側でもPythonコードを読み書きできることで実現できるシミュレーションの幅が大きく広がります。

ノン・プログラマーの方に向けて、Pythonの「ソースコードの読み方」を解説している本講座ですが、前回は、Pythonスクリプトを構成する要素(単語(変数=オブジェクト)と ( ) や. : [ ] などの記号)を紹介し、それぞれの「読み方」を紹介しました。 2回目の今回はもう少し踏み込んで、Pythonで重要な概念である「class(クラス)」に関わるソースコードについて、その読み方を紹介したいと思います。 Pythonに慣れていらっしゃらない方で、第1回をまだお読みになられていない方はこちらを先にご覧いただくことをお薦めいたします。

例文

さて、今回の例文は以下です(S4のエージェントシミュレーションを行うときに生成されるソースコードの一部になります):

class _Agent (SFMAgentBase):
    def initAfter(self, *args, **keys):
        """エージェント初期化後に呼ばれる。"""
        pass
    def step(self):
        """エージェントのステップ処理を記述する。SFM に従った基本的な動作を行った後に呼ばれる。"""
        if self.isStopping(): # エージェントが停止状態(目的地に到着)
            # エージェントを削除する:
            self.agentset.remove(self)
        elif self.isInErrorState(): # エラーが発生(目的地に到達不可など)
            print((self.state.message))
            # エージェントを削除する:
            self.agentset.remove(self)
        else: # それ以外の場合は、目的地に移動中
            pass


例によってこの時点で理解できなくて全く問題ありません! 前回には登場しなかった要素として、次のようなものがありますね:

  • キーワード "pass", "print"
  • キーワード "class"
  • キーワード "def" から始まる行
  • 何度か現れる "self" という単語

これらのうち、"pass", "print" については前回の応用で、そのままで読むことができます。それぞれ「何もしない」「(画面に)表示する」という意味になります(わざわざ「何もしない」を意味するキーワードが用意されているのも不思議ですが、これがあるとコーディングで便利なのです)。 残りの3つはすべて「クラス」というものに関わる内容になっています。

全体像

上のコードを翻訳すると、以下のようになります:

class クラス名 (継承先):
    def メソッド名1(self, 引数):
        """コメント"""
        # 処理1
    def メソッド名2(self):
        """コメント"""
        # 処理2


これはPythonの「クラス定義」を行うコードの構文になっています。
ざっくりいうと

(クラス名)というクラスで、(メソッド名1)というメソッド(引数はselfや~をとる)と(メソッド名2)というメソッド(引数はselfのみ)を持っているものを定義する

という読み方になります。
以下、「クラス」「メソッド」という言葉のいみやキーワードclass, def ,selfについてもう少し詳しく見ていきましょう。

"class"による「クラス」定義

改めて、まずはコードの1行目を見てみます:

class _Agent (SFMAgentBase):


ここでは"class"キーワードを用いて「クラス」定義を行っています。 「クラス」というのは色々な属性(内部状態)とメソッド(操作)をひとつにまとめたもので、Pythonプログラミングの根幹を成します。 第1回の記事で

Pythonではすべて「オブジェクト」というモノが動いている

という話をしましたが、この「モノ」がクラス(正確には、定義したクラスを実体化したもの)に相当します。

S4(psim)においてもエージェントシミュレーションのエージェントや環境、離散イベントシミュレーションのファシリティやストア、データを記録するモニタなど、全てクラスを使って記述しています。
使うには若干の慣れが必要ですが、使ったことが無くてもソースコードを「読む」ことはできます:

class A (B):


Bという既存のクラスを継承してAというクラスを作る

と読めばよいです。
「継承」とは既存のクラスを拡張する、という程度の理解で大丈夫です。S4を使う場合、ご自身で一からクラスを定義することは基本的には無く、GUI部品内のエディタでコード編集することでクラスのメソッドの中身を作り込んだり、カスタムコードから新しくメソッドを作ったり、という使い方をすることが多いです。

さて、この1行だけでは名前をつける以上のことはしていないので、次のメソッド定義によってクラスに意味を与えていくことになります。

"def" による「メソッド」定義~"self"の意味と使い方

    def initAfter(self, *args, **keys):
        """エージェント初期化後に呼ばれる。"""
        pass
    def step(self):
        """エージェントのステップ処理を記述する。SFM に従った基本的な動作を行った後に呼ばれる。""" 
        if self.isStopping(): # エージェントが停止状態(目的地に到着)
            # エージェントを削除する:
            self.agentset.remove(self)
        elif self.isInErrorState(): # エラーが発生(目的地に到達不可など)
            print((self.state.message))
            # エージェントを削除する:
            self.agentset.remove(self)
        else: # それ以外の場合は、目的地に移動中
            pass


"def"キーワードを用いてメソッド定義を行っています。
メソッド、というのは一連の処理をひとつにまとめて名前をつけたもののことで、ここでは_AgentクラスにinitAfterという名前のメソッドと、stepという名前のメソッドを定義しています。
メソッドは呼ぶときに変数を与えることができ、「何に対して処理を行うか」を指定することができます。

def M(self, a, b):


と書くことで

Mという名前で、引数として(selfに加えて)a,bをとるメソッドを作る

のように読みます。
コード例では

def initAfter(self, *args, **keys):


のように書かれており、引数が少し複雑に見えます。この中で重要なのは最初の"self"で、残りの*args, **keysは今回は気にしなくて良いです(可変長引数というもので、しばしばこういう書き方をすることがある、という程度で大丈夫です)。

さて、"self"はクラスでメソッドを定義する際に最初に与えられるもので、クラスを使う/作る際に重要な役割を果たします。
上記のstepメソッドにおいては

self.isStopping()
self.agentset.remove(self)



といった形でこの引数selfが使われていますが、"self"は読み方としては「自分自身」のように解釈すればよいです。ですのでこれらの文は

自分自身のisStoppingメソッドを実行している 自分自身のagentsetというモノの、removeというメソッドに対して引数としてselfを与えて実行している

のように読むことができます。
(ピリオド.でつなげるとそのモノの「所有物」にアクセスできて、カッコ()をつけるとそれに仕事をさせる、というのは前回の内容でしたね。)

ここは_Agentクラスの定義の中なので、ここでいうselfは_Agentクラスにおける「自分自身」、すなわち(エージェントシミュレーションでいうところの)エージェント、ということになります。
また、self.isStopping()というメソッドは_Agentクラスのメソッドということになります。

(が、今回のコードの中にはisStoppingメソッドの定義はありません。それがどこにあるかというと最初に現れた「継承」先であるSFMAgentBaseクラスになります。これはpsimが提供しているクラスなので、S4のソースコードの中に存在しています。)

少々説明が長くなってしまいましたが、重要なのは

1. クラスにメソッドを定義する際は基本的に最初の引数はselfにする
2. このselfを経由して自分自身や継承先のクラスのメソッド(↑のisStopping)や属性(↑のself.agentset)にアクセスすることができる

ということになります。
特にS4でコーディングを行う場合は2の使い方を頻繁に行うことになります
デフォルトで入っているコードにも頻繁にselfが現れますし、コード編集でself.~~として必要な機能を呼び出すことも基本的な使い方になります(どのようなメソッドが用意されているかは、サンプルコードや言語リファレンスを参照することで調べることができます)。

注意として、selfの意味はそれが使われているクラスに従って読み替える必要があります。S4ですとそのメソッドがエージェントなのか、エージェント集合なのか、環境なのかに応じてselfの指すものが変わりますので注意しながらコードを読むようにしてください。

補足

"def"キーワードは必ずしもクラス定義の中でしか使えないわけではなく、外に書くことも可能です。この場合は特定のクラスとは結びつかないので引数の最初をselfにする必要はなく、単に一連の処理をまとめたもの(「関数」)として扱われることになります。

おわりに

今回はPythonのクラス定義に関するコードの読み方を解説しました。 S4のpsimに限らず、Pythonは非常に豊富なライブラリが提供されています。今後プログラミングをより本格的に始め、これらを使う際には今回の内容を抑えておくだけでも理解しやすくなるのではないでしょうか。