Pythonのオブジェクト指向はどのように書くの?
Pythonにおけるオブジェクト指向の活用例や学び方は?
「オブジェクト指向」は、人気のプログラミング言語「Python」にも欠かせない考え方です。しかし、Pythonを学ぶうえでオブジェクト指向の書き方がわからず、上記のような疑問を抱えている人も多いですよね。
そこで、本記事では基本の概念や活用例も交え、Pythonのオブジェクト指向プログラミングをわかりやすく解説します。Pythonにおけるオブジェクト指向の適した使い方・手法や学習方法も紹介するので、ぜひ参考にしてください。
なお、本記事は変数や処理分岐・繰り返しといったPythonの基本文法を最低限は理解している人が対象です。
オブジェクト指向とは「開発効率を高める考え方」
オブジェクトは「モノ」を意味します。つまりオブジェクト指向とは、プログラムの要素をオブジェクト(モノ)として扱う考え方です。人間や動物、機械など、プログラムの要素は何であれオブジェクトとして扱います。
オブジェクト指向では「クラス」が欠かせません。クラスとは、モノが持つ特徴や機能・動作を汎用的な形にまとめた「ひな型」です。クラスというひな型をもとにさまざまなオブジェクトを作り、操作していきます。
例えば「動物」クラスを作れば、「犬」や「猫」といったオブジェクトを作って扱うことが可能です。動物クラスを作ってしまえば、扱う動物が増えても使い回せます。そのため、効率的にプログラムを開発できるのです。
以前は、プログラムの手続き(関数)を順番に並べていく「手続き型プログラミング」が主流でした。しかし大規模なチーム開発だとプログラムが複雑になる、同じ処理を何度も記述することになる、など多くの問題が生じました。
そこで、開発効率を高めるために生まれた考え方がオブジェクト指向です。オブジェクト指向だと、クラスによってプログラムの再利用や変更が容易に行えます。そのため、チーム開発を効率的に行える手法として普及しました。
なお次の記事ではそもそもオブジェクト指向とは何なのか、そのメリットを具体例も交えて解説しているので、よければ参考にしてください。
Pythonのオブジェクト指向におけるクラスの主な構成要素
Pythonのオブジェクト指向を理解するうえでは、クラスの理解が欠かせません。オブジェクト指向におけるクラスの主な構成要素として、次の3つを知っておきましょう。
今回は、時計クラス(clockクラス)を例に考えます。時計クラスの特徴・機能は次のとおりです。
- 現在の「時」と「分」を内部で管理する。
- 現在時刻を設定・表示する機能がある。
これらを踏まえると、簡単な時計クラスのサンプルコードは次のようになります。
# 時計クラス class Clock: # コンストラクタ(初期化処理) def __init__(self): self.hour = 0 # 時 self.minute = 0 # 分 # 現在時刻を設定するメソッド def set_time(self, hour, minute): # 引数で渡された時・分が範囲内かチェック(時:0~11、分:0~59) if (0 <= hour < 12) and (0 <= minute < 60): self.hour = hour self.minute = minute print(f"時刻を{self.hour}:{self.minute:02}に設定しました。") else: print("時刻の設定が正しくありません。") # 現在時刻を表示するメソッド def show_time(self): print(f"ただいまの時刻は{self.hour}:{self.minute:02}です。") # 時計クラスのインスタンス(実体)を生成 my_clock = Clock() # 時計を10:30に設定 my_clock.set_time(10, 30) # 現在の時刻を表示 my_clock.show_time()
このサンプルコードをもとに、3つの構成要素について順番に解説します(クラスを使う流れは後述)。
属性
「属性」とは、クラスから作られたオブジェクトが内部で管理する変数(データ)のことです。時計クラスでいえば、「時」「分」といった時計が共通して持っている時刻情報が、属性にあたります。
Pythonのクラスにおいて、属性はself.属性名のように表現します。selfは「そのオブジェクト自身」のことです。Pythonでは、クラス内部で自身のデータを参照するときにselfを使うことを覚えておきましょう。
上記のサンプルコードでは、以下の2つが時計クラスの属性である「時」と「分」です。
self.hour # 時 self.minute # 分
例えばself.hourが「2」、self.minuteが「38」なら、時計クラスのオブジェクトは「2時38分」を指します。このように、属性を用いてオブジェクトが持つデータの管理が可能です。
メソッド
「メソッド」とは、クラスから作られたオブジェクトが持つ機能や動作のことです。時計クラスでいえば、「現在時刻を設定する機能」「現在時刻を表示する機能」がメソッドにあたります。
メソッドは、Pythonの関数と同じように「def」を用いて定義します。ただし注意点として、第一引数は必ずselfとしなければなりません。このselfを用いて、オブジェクト自身の属性にアクセスするわけです。
上記のサンプルコードでは、set_time(時刻設定)とshow_time(時刻表示)が時計クラスのメソッドです。
# 現在時刻を設定するメソッド def set_time(self, hour, minute): # 引数で渡された時・分が範囲内かチェック(時:0~11、分:0~59) if (0 <= hour < 12) and (0 <= minute < 60): self.hour = hour self.minute = minute print(f"時刻を{self.hour}:{self.minute:02}に設定しました。") else: print("時刻の設定が正しくありません。") # 現在時刻を表示するメソッド def show_time(self): print(f"ただいまの時刻は{self.hour}:{self.minute:02}です。")
set_time()メソッドは、引数の時・分が正しい場合にのみ、属性のself.hourとself.minuteが持つ現在時刻を更新しています。show_time()メソッドは、各属性のデータを表示するのみです。
このようにメソッドを定義すれば、生成されたオブジェクトが自由にその機能・動作を使えます。
コンストラクタ
「コンストラクタ」とは、クラスからオブジェクトを作るときのみ呼び出される特殊なメソッドです。コンストラクタは、主に属性を定義・初期化するために使います。
Pythonのコンストラクタは、「__init__」という名前で定義するのが基本です。前述のメソッドと同様、第一引数はselfとしなければなりません。上記のサンプルコードでは、次のメソッドがコンストラクタです。
# コンストラクタ(初期化処理) def __init__(self): self.hour = 0 # 時 self.minute = 0 # 分
このコンストラクタでは、「時」と「分」の属性を宣言し、ともに0で初期化しています。オブジェクトを作るとき、最初にこのコンストラクタが呼ばれることで属性が作られ、「0時00分」にセットされるわけです。
このように、コンストラクタを使うことでクラスのオブジェクトを初期化できます。
Pythonのオブジェクト指向におけるクラスを使う流れ
クラスを使うときの流れは、大まかに次の3ステップです。
- 1.クラスの定義
- 2.クラスのインスタンス化
- 3.インスタンスの参照
まずは、「class クラス名:」のようにクラスの定義が必要です。各メソッドは、そこからインデント(半角スペース4つ分)を1つ下げて記述してください。サンプルコードでは、下記の部分でクラスを定義しています。
# 時計クラス class Clock: ###(後略)###
次に、定義したクラスを「インスタンス化」します。インスタンス化とは、クラスからオブジェクトの実体(インスタンス)を生成すること。インスタンス化によって、新しいオブジェクトが作られるわけです。
インスタンス化は「インスタンス名 = クラス名( 値1, 値2…)」のように記述します。()内は、コンストラクタの引数にデータを渡すときに必要です。サンプルコードでは、下記の部分でインスタンス「my_clock」を生成しています。
# 時計クラスのインスタンス(実体)を生成 my_clock = Clock()
生成したインスタンスの参照は、「インスタンス名.メソッド名( 値1, 値2…)」のように記述します。メソッドの引数がselfのみなら()内は不要です。当然ながら、インスタンスが生成されていないとメソッドは参照できません。
サンプルコードでは、下記の部分で時計クラスのset_time()メソッドとshow_time()メソッドを参照しています。
# 時計を10:30に設定 my_clock.set_time(10, 30) # 現在の時刻を表示 my_clock.show_time()
なお、サンプルコードの実行結果は次のとおりです。set_time()メソッドで時刻が設定され、show_time()メソッドで設定後の現在時刻が表示されています。
時刻を10:30に設定しました。 ただいまの時刻は10:30です。
このように、クラス定義→インスタンス化→参照という流れで、時計というオブジェクトを扱えるのです。なお、Pythonのクラスについて詳しく知りたい人は、次の記事も参考にしてください。
Pythonにおけるオブジェクト指向の特徴3つ
Pythonに限った話ではありませんが、オブジェクト指向には次の3つの特徴があります。
先ほどのサンプルコードも踏まえて、順番に解説します。
プログラムが独立している
プログラムの要素を個々のクラス・オブジェクトとして扱うことで、独立させることが可能です。つまり、オブジェクトが他オブジェクトに影響を与えたり、他オブジェクトから影響を受けたりしにくいといえます。
例えば、「時計クラス」と「温度計クラス」が1つのプログラムに共存していたとしましょう。このとき、時計クラス内のメソッドを変更しても、温度計クラスのオブジェクトの動作が変わることは基本的にありません。
一般的に、プログラムにさまざまな変更を加えていくうえで、思わぬ部分に影響が生じてしまうリスクがあります。その点、プログラムが独立しているオブジェクト指向であれば、変更にともなう影響のリスクを減らせるのです。
なお、後述する「カプセル化」という仕組みを使うことで、より独立性を高められます。
プログラムを再利用しやすい
前述のとおり、プログラムを再利用しやすいのがオブジェクト指向の強みです。クラスというひな型さえ作れば、手軽に複数のオブジェクト(インスタンス)を生成して、それぞれを独立的に扱えます。
例えば、1つの時計クラスから「腕時計」「目覚まし時計」といった複数のインスタンスを生成可能です。時計の特徴や機能が共通していれば、基本的に同じクラスを使い回すことでプログラムの記述量を減らせます。
また、後述するクラスの「継承」という仕組みで、別のクラスへ派生させることも可能です。例えば、時計クラスを継承させて「デジタル時計クラス」「アナログ時計クラス」といった派生クラスを作れます。
プログラムを改変しやすい
プログラムを改変しやすいのもオブジェクト指向の強みです。同じクラスから生成したオブジェクトは、すべて同じ特徴や機能を引き継ぎます。そのため、特徴や機能を変えたい場合でも、1つのクラスだけを改変すれば済みます。
例えば、時計クラスから生成された「腕時計」「目覚まし時計」の表示仕様を変えたいとします。この場合、時計クラスのshow_time()メソッドだけを変更すれば、すべての時計オブジェクトの仕様変更が可能です。
また後述する「オーバーライド」という仕組みを使えば、継承元のクラスから変更が必要なメソッドだけを上書きできます。例えば、時計クラスを継承した「デジタル時計クラス」のshow_time()メソッドだけを上書き可能です。
Pythonにおけるオブジェクト指向の基本手法
Pythonにおけるオブジェクト指向の基本手法として、次の3つを知っておきましょう。
特徴の部分でも軽く触れましたが、ここではサンプルコードを交えて具体的に紹介します。
プログラムの独立性を高める「カプセル化」
オブジェクト指向の「カプセル化」という仕組みによって、プログラムの独立性を高めることが可能です。カプセル化とは、関連するプログラム要素を1つのカプセルにまとめ、外部からの直接的なアクセスを防止すること。
わかりづらいため、具体例を挙げましょう。先ほどの時計クラスでは、set_time()メソッドで時刻を設定しました。しかし、次のコードを見てください。
# 時計クラス class Clock: # コンストラクタ(初期化処理) def __init__(self): self.hour = 0 # 時 self.minute = 0 # 分 ###(中略)### # 時計クラスのインスタンス(実体)を生成 my_clock = Clock() # 【非推奨】時計の時刻を直接100に変更 my_clock.hour = 100 # 現在の時刻を表示 my_clock.show_time()
下記の部分ではset_time()メソッドを使わず、時刻を外部から直接変更しています。現状のコードはカプセル化されていないため、このような変更がまかり通ってしまうのです。
# 【非推奨】時計の時間を直接100に変更 my_clock.hour = 100
上記コードの実行結果は、次のとおり。時刻が不正に変更されたことで、「100:00」と表示されてしまいました。
ただいまの時刻は100:00です。
こうした予期しない変更を防ぐための仕組みがカプセル化です。Pythonでカプセル化を取り入れる場合は、クラス内の属性名の頭に「__(アンダーバー2つ)」を付けましょう。具体的には、次のようになります。
# 時計クラス class Clock: # コンストラクタ(初期化処理) def __init__(self): self.__hour = 0 # 時 self.__minute = 0 # 分 ###(中略)### # 時計クラスのインスタンス(実体)を生成 my_clock = Clock() # 【不可】時計の時刻を直接100に変更 my_clock.__hour = 100 # 現在の時刻を表示 my_clock.show_time()
時計クラス内の属性が「__hour」「__minute」となっています。このようにアンダーバーが2つ付いた属性は、外部から変更できません。そのため、「my_clock.__hour = 100」といった不正な変更を防ぐことが可能です。
修正したコードの実行結果は、次のようになります。不正な変更が行われず、初期値が表示されました。
ただいまの時刻は0:00です。
ただし、外部から属性にアクセスできない代わりに、属性を設定・参照するためのメソッドが必要です。時計クラスでいえば、set_time()メソッドとshow_time()メソッドが該当します。
特に設定用のメソッドでは次のように、引数の設定値に問題ないかしっかりチェックしましょう。チェックが正しくないと、誤ったメソッドの使い方をされて不正なデータが設定されてしまいます。
# 現在時刻を設定するメソッド def set_time(self, hour, minute): # 引数で渡された時・分が範囲内かチェック(時:0~11、分:0~59) if (0 <= hour < 12) and (0 <= minute < 60):
上記のset_time()メソッドで時刻を設定すれば、たとえ引数で「100時」などと指定されてもエラーではじけます。
時刻の設定が正しくありません。
このように、カプセル化によってクラス内のデータを外部から不正に書き換えられることを防止でき、プログラムの独立性が高まります。
プログラムの再利用性を高める「継承」
オブジェクト指向の「継承」という仕組みによって、プログラムの再利用性を高めることが可能です。継承とは、あるクラス(親クラス)の属性やメソッドを受け継いだ派生クラス(子クラス)を作成すること。
Pythonでは、「class 子クラス名(親クラス名):」のように記述することで、クラスを継承できます。例えば次のサンプルコードでは、時計クラスを継承させた「デジタル時計クラス」を定義しています。
# 時計クラス class Clock: # コンストラクタ(初期化処理) def __init__(self): self.__hour = 0 # 時 self.__minute = 0 # 分 ###(中略)### # デジタル時計クラス(時計クラスを継承) class DigitalClock(Clock): pass # 何も変更せず、親クラスのまま # デジタル時計クラスのインスタンス(実体)を生成 my_digital_clock = DigitalClock() # デジタル時計を11:45に設定 my_digital_clock.set_time(11, 45) # 現在のデジタル時刻を表示 my_digital_clock.show_time()
時計クラスの定義後、下記の部分で子クラスのデジタル時計クラスを定義しています。何も変更させる必要がない場合は、このように「pass」と記述しましょう。そうすれば、親クラスの属性やメソッドをそのまま受け継ぎます。
# デジタル時計クラス(時計クラスを継承) class DigitalClock(Clock): pass # 何も変更せず、親クラスのまま
上記コードの実行結果は次のとおりです。親クラスである時計クラスを継承したため、デジタル時計クラスのインスタンスも同様のメソッドを実行できました。
時刻を11:45に設定しました。 ただいまの時刻は11:45です。
このように、クラスの継承を行うことで親クラスの属性やメソッドを再利用できます。なお、Pythonにおけるクラスの継承については次の記事で詳しく紹介しているので、あわせて参考にしてください。
プログラムの拡張性を高める「オーバーライド」
オブジェクト指向の「オーバーライド」という仕組みによって、プログラムの拡張性を高めることが可能です。オーバーライドとは、親クラスのメソッドを子クラスで上書き(再定義)すること。
例えば、時計クラスを継承したデジタル時計クラスは「24時間表示」という仕様であるとしましょう。24時間表示であれば、次のように「23:45」は問題なく設定できるはずです。
# デジタル時計を23:45に設定 my_digital_clock.set_time(23, 45)
しかし実際には、次のように正しく表示されません。
時刻の設定が正しくありません。 ただいまの時刻は0:00です。
これは、親クラスである時計クラスのset_time()メソッドで、「時」が0〜11の範囲内でなければ不正と判定しているためです。デジタル時計クラスは仕様が異なるために、親クラスの時刻設定処理では対応できません。
# 現在時刻を設定するメソッド def set_time(self, hour, minute): # 引数で渡された時・分が範囲内かチェック(時:0~11、分:0~59) if (0 <= hour < 12) and (0 <= minute < 60):
こうしたケースで役に立つのがオーバーライドです。Pythonでメソッドをオーバーライドする場合、子クラスの定義内で親クラスと同名のメソッドを再定義しましょう。サンプルコードは次のとおりです。
# 時計クラス class Clock: # コンストラクタ(初期化処理) def __init__(self): self.__hour = 0 # 時 self.__minute = 0 # 分 ###(中略)### # デジタル時計クラス(時計クラスを継承) class DigitalClock(Clock): # 引数で渡された時・分が範囲内かチェック(時:0~23、分:0~59) def set_time(self, hour, minute): if (0 <= hour < 24) and (0 <= minute < 60): self.__hour = hour self.__minute = minute print(f"時刻を{self.__hour}:{self.__minute:02}に設定しました。") else: print("時刻の設定が正しくありません。") # 現在時刻を表示するメソッド def show_time(self): print(f"ただいまの時刻は{self.__hour}:{self.__minute:02}です。") # デジタル時計クラスのインスタンス(実体)を生成 my_digital_clock = DigitalClock() # デジタル時計を23:45に設定 my_digital_clock.set_time(23, 45) # 現在のデジタル時刻を表示 my_digital_clock.show_time()
デジタル時計クラス(DigitalClock)の定義で今回は「pass」とせず、set_time()メソッドとshow_time()メソッドを再定義しています。次のように、if文のチェック条件が(0 <= hour < 24)と改変されているのがポイントです。
# 引数で渡された時・分が範囲内かチェック(時:0~23、分:0~59) def set_time(self, hour, minute): if (0 <= hour < 24) and (0 <= minute < 60): self.__hour = hour self.__minute = minute print(f"時刻を{self.__hour}:{self.__minute:02}に設定しました。") else: print("時刻の設定が正しくありません。") # 現在時刻を表示するメソッド def show_time(self): print(f"ただいまの時刻は{self.__hour}:{self.__minute:02}です。")
親クラスからの変更がないメソッドは再定義する必要がありません。ただし注意点として、オーバーライドしなかったメソッド内で属性を参照している場合、親クラスのデータをそのまま引き継ぎます。
例えば、表示処理のshow_time()メソッドをオーバーライドしなかった場合、次のように時刻表示が正しく更新されません。これは、show_time()メソッドで表示している属性が親クラスのデータ(=初期値のまま)となるため。
時刻を23:45に設定しました。 ただいまの時刻は0:00です。
そのため、show_time()メソッドもデジタル時計クラスで改めて定義しなければなりません。下記の定義は親クラスとまったく変わりませんが、参照している属性(__hour、__minute)が子クラスのデータとして表示されます。
# 現在時刻を表示するメソッド def show_time(self): print(f"ただいまの時刻は{self.__hour}:{self.__minute:02}です。")
set_time()メソッドとshow_time()メソッドを正しくオーバーライドすれば、次のように24時間対応の時刻表示が可能となります。このように、オーバーライドによって親クラスのメソッドを改変することが可能です。
時刻を23:45に設定しました。 ただいまの時刻は23:45です。
なお次のように、親クラスのインスタンス「my_clock」と子クラスのインスタンス「my_digital_clock」を混在させても問題ありません。
# 時計クラス・デジタル時計クラスのインスタンス(実体)を生成 my_clock = Clock() my_digital_clock = DigitalClock() # 時計を23:45に設定 my_clock.set_time(23, 45) my_digital_clock.set_time(23, 45)
上記コードの実行結果は次のとおりです。親クラス(my_clock)は24時間表示が許容されず、子クラス(my_digital_clock)は24時間表示を許容しています。
時刻の設定が正しくありません。 時刻を23:45に設定しました。
このように、同名のメソッド名でもクラスによって振る舞いが変わることを、オブジェクト指向では「ポリモーフィズム(多態性)」と呼びます。カプセル化・継承・ポリモーフィズムは、オブジェクト指向の3大要素です。
Pythonにおけるオブジェクト指向プログラミングの活用例
Pythonのオブジェクト指向は、さまざまな開発分野でプログラミングに活用されています。
Pythonでのプログラム開発では、ライブラリ(再利用できるプログラムの部品)やフレームワーク(プログラムの枠組み)の活用が欠かせません。そして、それらの多くはオブジェクト指向を採用しているのです。
例えば、次のPythonライブラリ・フレームワークは、いずれもオブジェクト指向を採用しています。
- Django:Web開発向けフレームワーク
- scikit-learn:AI開発向けライブラリ
- beautifulsoup:スクレイピング(Webデータの取得)向けライブラリ
こうした要素を活用して効率的にプログラムを開発するためには、オブジェクト指向の知識が必須です。
Pythonのオブジェクト指向プログラミングを学ぶ方法
Pythonスキルを身につけるうえで、オブジェクト指向の学習は避けて通れません。しかし、どのように学ぶべきか迷っている人も多いのではないでしょうか。オブジェクト指向プログラミングを学ぶ方法は、主に次の2つです。
独学で勉強する
独学で勉強する場合、入門サイトや入門書などの教材を用いて自分だけで学習を進めます。自分のペースで学べることや、成功すれば費用を抑えられることが独学のメリットです。
ただし、教材選びはもちろん学習計画、進捗管理、問題点の原因調査など、すべてを自分でカバーすることが求められます。プログラミング経験が少ない人だと、独学で上手くいかず挫折してしまうケースが少なくありません。
事実、弊社がプログラミング学習の経験者298人に対して実施したアンケートでは87.5%が学習中に「挫折や行き詰まりを感じたことがある」と回答しました。
このように、プログラミングの独学はPythonに限らず、挫折のリスクが高いです。確実にPythonのオブジェクト指向を学びたい人は、ほかの学習方法も考えることをおすすめします。
プログラミングスクールで学ぶ
プログラミングスクールで学ぶ場合は、講師から教わりながらプログラミング学習を進めます。スクールを利用するための費用はかかるものの、挫折せず短期間でスキルを習得できるのが大きなメリットです。
Pythonのオブジェクト指向は学ぶべきことが多く、一朝一夕でマスターできるほど簡単ではありません。これまでの内容でも、少なからず難しさを感じた人は多いのではないでしょうか。
その点、スクールであればプログラミングの経験が豊富な現役エンジニアの講師が教えてくれます。不明点があってもすぐに質問できるため、プログラミングの経験がなくても挫折せずに学習を進めていけます。
なかでも、侍エンジニアは現役エンジニア講師からマンツーマン指導が受けられるオンラインスクールです。オーダーメイドのカリキュラムで、受講者のニーズにあわせて柔軟に学び進められます。
また、経済産業省「リスキリングを通じたキャリアアップ支援事業」の対象スクールでもあります。受講料の最大70%(上限56万円)が支給される可能性があるため、費用を抑えたい人にもおすすめです。
Pythonのオブジェクト指向を挫折せず習得したい人は、お気軽にご利用ください。
公式サイトで侍エンジニアの詳細を見るまとめ
今回はPythonのオブジェクト指向に関して、次の6点についてお伝えしました。
オブジェクト指向は、さまざまなプログラミング言語で広く採用されている考え方です。Pythonでも、効率的に開発を進めていくうえではオブジェクト指向の理解が欠かせません。
ただし、Pythonのオブジェクト指向は学ぶべきことが多いため、挫折しやすいポイントでもあります。独学に不安がある場合はスクールの利用も検討しましょう。
Pythonのオブジェクト指向を学ぶときには、今回の内容をぜひ参考にしてください。