オブジェクト指向を定義することは、恐れ多くて私には出来ません。
ウィキペディアによると以下のような定義になっています。
オブジェクト指向(オブジェクトしこう)とは、オブジェクト同士の相互作用として、システムの振る舞いをとらえる考え方である。
一般的にJavaがオブジェクト指向の代表言語ですが、Pythonもオブジェクト指向言語です。
そこで今回はオブジェクト指向三原則と言われている、「継承」「ポリモーフィズム」「カプセル化」について見てみたいです。
継承
私が最初にクラスの勉強をした時は、まったく意味不明でした。しかし継承の勉強をすることで、クラスの便利さに気が付きました。私にとっては、クラスよりも、継承の方がわかりやすかったです。
思ったよりも簡単な継承
継承元のクラスを親クラスといい、継承先のクラスを子クラスと言います。
親クラスの特徴を子クラスに引き継ぐことが継承です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#Test1クラス class Test1: def test_method(self): return "Test1クラスで定義したメソッド" #Test2クラスに(Test1)と記述してTest1クラスを継承させる class Test2(Test1): #passは何もしないときに記述する文 pass #Test2クラスをインスタンス化 a = Test2() #Test2クラスにメソッドはないのにもかかわらず「継承」しているので使える print(a.test_method()) |
結果
Test1クラスで定義したメソッド
ポリモーフィズム
ポリモーフィズムの定義も難しいのですが、ウィキペディアによるとこうなります。
ポリモーフィズムとは、プログラミング言語の型システムの性質を表すもので、プログラミング言語の各要素についてそれらが複数の型に属することを許すという性質を指す。
この定義によると、Pythonはデータ型に緩いので、何もしなくてもポリモーフィズムになっていると言えるかも知れません。
Javaの場合、データ型に厳しいので、親クラスまたはインタフェースを子クラスで実装して、子クラスをインスタンス化する際に、親のデータ型に揃えます(アップキャスト)。そのオブジェクトは親の型なので、親クラスの型を指定した配列やリストに入れることができます(Javaはあらかじめ決めたデータ型しか入れられませんが、Pythonは基本的に何でも入るのでデータ型を揃える必要がない)。そのようなオブジェクト群に、同名メソッドを呼び出すことで、同名メソッドにもかかわらず、振る舞いが変えられます。
同名のメソッドを呼び出すが、オブジェクトによってその振る舞いが違うということがポイントです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
class Greeting: # 親クラス def say(self): return "Hello" class English(Greeting): # 何もしていないが、継承しているので、親クラスのsayメソッドが使える pass class Japanese(Greeting): # 親クラスのsayメソッドをオーバライドしている def say(self): return "こんにちは" class Arabic(Greeting): # 親クラスのsayメソッドをオーバライドしている def say(self): return "مرحبا هناك" class Turkish(Greeting): # 親クラスのsayメソッドをオーバライドしている def say(self): return "Merhaba" class Russian(Greeting): # 親クラスのsayメソッドをオーバライドしている def say(self): return "привет там" class French(Greeting): # 親クラスのsayメソッドをオーバライドしている def say(self): return "Bonjour" class German(Greeting): # 親クラスのsayメソッドをオーバライドしている def say(self): return "Hallo" # データ型がバラバラでもタプルに入る t = ( English(), Japanese(), Arabic(), Turkish(), Russian(), French(), German() ) for v in t: # 同じメソッド名だが、オブジェクトによってその振る舞いが違う print(v.say()) |
結果
Hello
こんにちは
مرحبا هناك
Merhaba
привет там
Bonjour
Hallo
オーバーライドとは、親クラスにあるメソッドと同名のメソッドを子クラスで定義すると、子クラスのメソッドが親クラスのメソッドを上書きすることです。
super()で親クラスのメソッドを使う
同名メソッドでオーバーライドされるのですが、親クラスのメソッド内容も使いたい場合もあります。
Test2クラスにコンストラクタ(__init__のメソッド)を使用すると、親クラスのコンストラクタを上書きしてしまうので、self.nameのインスタンス変数が設定されません。そこで、Test2クラスのコンストラクタ内の「super().__init__()」部分で、親クラスのコンストラクタを呼び出しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Test1: def __init__(self): self.name = "ライブインホープ" def get_name(self): return self.name class Test2(Test1): def __init__(self): super().__init__() self.type = "B型事業所" def get_type(self): return self.type #Test2クラスをインスタンス化 a = Test2() print(a.get_name()) print(a.get_type()) |
結果
ライブインホープ
B型事業所
カプセル化
カプセル化とは、クラスの変数やメソッドを外部から自由にアクセスすることを禁止して、クラス作成者の意図した手続きでしか利用することができないようにすることです。それにより個々のオブジェクトの誤動作を減らし、クラス作成者の意図しない使われ方を防止します。
Pythonのカプセル化は、Javaとは違い、紳士協定のような感じで行います。
Python3メモ⑫(クラス)の時も書きましたが、アンダースコア(_)で始まる変数や関数は外から参照しないという慣習があり、アンダースコア2個(__)で始まる変数や関数は参照制限されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Test: def __init__(self): self.name = "ライブ" self._name = "イン" self.__name = "ホープ" obj = Test() # これは参照してよい print(obj.name) # 代入してよい obj.name = "live" print(obj.name) # これは参照できるけれど、参照してはいけないルール print(obj._name) # 代入もできるけどしない obj._name = "in" print(obj._name) # これは参照できないけれど # print(obj.__name) # こうすれば参照できるけれど、参照してはいけないルール print(obj._Test__name) # 代入もできるけどしない obj._Test__name = "hope" print(obj._Test__name) |
結果
ライブ
live
イン
in
ホープ
hope