Python オブジェクト指向 入門

python

Pythonにおけるオブジェクト指向について解説【入門編その②】

2020年3月7日

こんにちは。

 

この記事ではPythonにおけるオブジェクト指向の2部構成のうち、後半となります。

 

前回のPythonにおけるオブジェクト指向の前半部分については以下の記事を参照にしてください。

関連記事
Python オブジェクト指向 オブジェクト指向プログラミング
Pythonにおけるオブジェクト指向について解説【入門編その①】

続きを見る

 

今回は、オブジェクト指向の特徴である以下3つについて説明していきます。

 

 本記事の学習目標

  • 継承
  • 多様性
  • カプセル化

 

これらのコードの書き方に対する考え方は必須ですので、しっかり習得していきましょう。

オブジェクト指向の特徴 「継承」

Python オブジェクト指向 入門

継承

まずは、継承についてです。

 

継承とは、あるオブジェクトが他のオブジェクトの特性を引き継ぐ事です。
» wikより引用

 

しかし、単に引き継ぐと言われてもわかりにくいと思います。

「継承」について具体的に考える

継承とは具体的に、前回の犬のオブジェクトから考えると、その上位概念になるものを考えます。

 

また、あるクラスの特徴を他のクラスが受け継いだ上で、さらに他の特徴も加わることを言います。

 

A というクラスがあり、B というクラスが A を継承する場合、A を親クラス(スーパークラス)、B を子クラス(サブクラス) と呼んでいます。

 

継承を利用して、定義がほぼ同じのクラスは、その定義を親クラスに集約させ、子クラスで継承するという形でコードを記述すれば、同じようなことを何度も書く面倒さが無くなります。

 

上位概念としては、「動物」だと考え易いかと思います。

 

「動物」というオブジェクトには動物全てで考えられる属性振る舞を設定し、そして、それを継承します。(特性を引き継ぎます。)

 

「犬」「鳥」などにはそれぞれ固有の属性と振る舞いを設定します。

 

この「動物」「犬」「鳥」は以下のように呼ばれます。

「動物」を親クラス(スーパークラス、基底クラス)

「犬」「鳥」を子クラス(サブクラス、派生クラス)

具体的には下記図のような関係になります。

Python オブジェクト指向 入門

 

矢印は、親クラスを参照しますという意味になります。

 

Animal としての属性である name や、振る舞いの sleep は子クラスの Dog , Bird でも使えます。

 

しかし、それぞれで特有な DogbreedBirdfly() はそれぞれのクラスでしか使用ができません。

 

ですので、Dog のオブジェクトで fly() は使えずエラーになります。

 

Python オブジェクト指向 入門

次のコード例で動かして確認してみましょう。

Classを作成

まずは親クラスである、Animal クラスを作りましょう。

 

前回の復習がてら、作ってみましょう。

 

コード例は下記ですが、参照しながら、実装してみてください。

 

In[]

この Animal Class は前回の Dog Class 同様普通に使うことができます。

 

使いかも復習がてら、実装してみましょう。

In[]

Out[]

どうでしょう?思い出してきたところで、新しい要素である。継承を使っていきます。

 

継承の使い方

親クラスの宣言は特に気にする必要はありません。

 

子クラスを宣言するときに、親クラスを継承しますよという意味で、子クラスのあとの括弧書きに親クラスを入れます。

 

コンストラクタ時の引数は必要に応じて数は変わりますが、子クラスでは親クラスの引数も取得して、super().__init__ で親クラスの引数を割り当てる必要があります。

 

super() は親クラス(スーパークラス、基底クラス)のことになります。

 

In[]

 

Animal Classを継承してDog Classを作成

まずAnimal Classを継承して、Dog Classを作成します。

 

記述ルールに従って、以下の様に作成します。

  • 子クラスの後のカッコ書きに親クラス名を追加 class Dog(Animal)
  • コンストラクトのところにsuper().__init__(name, weight)と親クラスのコンストラクタを追加

実際のコードは下記になります。

In[]

早速使ってみましょう。

 

In[]

Out[]

使い方は前回のDog Classと全く同じになりますが、今回はAnimal Classで定義したプロパティ、メソッドにもアクセスできることが分かりますね。

 

これが継承と呼ばれる仕組みです。

Animal Classを継承してBird Classを作成

同様にBird Classを実装します。Dog Classと同じように実装してみてください。

 

コード例、使い方は下記になりますので、わからなくなったら、参照してみてください。

In[]

 


Out[]

 

Dog Classのインスタンスでfly()を使ってみる

結果は予想通り、エラーになります。

In[]

Out[]

 

Birdにあるflyメソッドは当然、Dogには実装していないので、そんな物はないと怒られるわけです。

 

オブジェクト指向の特徴 「多態性」

Python オブジェクト指向 入門

Python オブジェクト指向 入門

次に多態性について解説していきます。多様性とはポリモーフィズム(polymorphism)とも言います。

 

多様性を簡単に定義すると以下の様な意味合いになります。

 

 オブジェクト指向の「多様性」の意味

  • 同じメソッドを使い、オブジェクトによって異なる「振る舞い」をすること。

 

多様性」を実現する方法に、次に説明するオーバーライドという方法あります。

 

オーバーライド

オーバーライドは子クラスで親クラスの宣言を上書きすることを言います。

 

言葉ではわかりにくいので、次で具体的に考えていきます。

オーバーライドの意味とは

多様性を実現する方法としてオーバーライドがあります。

 

オーバーライドは子クラスで親クラスの宣言を上書きすることです。

 

また、多様性とは同じメソッドを使用して、オブジェクトに異なる振る舞いをする事でした。

 

オブジェクトの「振る舞い」が異なるとは具体的にどういったことでしょうか。

 

例えば、動物は吠えたり鳴いたりします。

 

その振る舞いの事を make_sound() と表現ます。

 

鳥と犬では吠えたり、鳴いたりと違うので、make_sound() を実行しても異なる「振る舞い」の仕方になります。

 

鳥と犬との異なる振る舞いについて、以下の図を見て下さい。

Python オブジェクト指向 入門

以下がそのコードになります。

 

make_sound() が追加してあり、他の箇所は省略していますが、今まで書いてあるコードが記載されているとしてください。

 

オーバーライドのコードの書き方

実際にオーバーライドのコードの書き方を記します。

 

make_sound() が追加してあり、他の箇所は省略していますが、今まで書いてあるコードが記載されているとしてください。

 

具体的には、親クラスの Animal に make_sound() メソッドを追加します。

 

親クラスのAnimalに子クラスでオーバーライドするための make_sound() メソッドを実装します。

 

子クラスでのオーバーライドを必ずして欲しい場合とどちらでも良い場合があります。

 

その時は、passraise NotImplementedErrorを使い分けてください。

  • オーバーライドしてもしなくても良い場合:pass
  • オーバーライドを必ずして欲しい場合:raise NotImplementedError

raise NotImplementedError とすると、子クラスでオーバーライドしていないで使うとエラーが発生するようになります。

子クラスのDog Classにmake_sound()をオーバーライドする

先ほど作成した、Dog Classに make_sound() をオーバーライドします。

 

オーバーライドは普通に関数を実装する方法と同じです。

In[]

 

さて、使ってみましょう。Dogをインスタンスして、make_sound() を使います。

 

In[]

Out[]

Dog Classでの make_sound() ではワン!ワン!と吠える動作を実装できました。

 

次は、Bird Classにも実装します。

子クラスのBird Classにmake_sound()をオーバーライドする

同様に、Bird ClassにもDog Class同様に make_sound() をオーバーライドして、使ってみてください。

 

下記が、コード例ですので、参照しながら実装してみてください。

 

In[]

 

Out[]

Bird Classではピー!ピー!と鳴く動作を実装できましたね。

 

print文のコメントが違うだけですが、このように、子クラスで異なる振る舞いをしたい場合にはオーバーライドを使って実装します。

 

オーバーロード

オーバーロードも多態性の一種だと言われていますが、最初に定義したものと少し意味合いが違ってきます。

 

オーバーロードは「引数、型などの違いで、同じメソッド名を複数つくること」を意味します。

 

例えば、下記のように func_sample という同じメソッド名ですが、引数が1つのものと2つのものを作るといったことになります。

In[]

 

三角関数の tan() を使用してオーバーロードを考えましょう。

  • 引数1つの場合:tan(x/y)
  • 引数2つの場合:tan(x, y)

 

オーバーロードを具体例で考える

犬のチョコちゃんに走ってもらうときに、「普通に走る」か「速く走ってもらうか」を指示するとします。

 

メソッドの run() の引数でその速さを指定しますが、速く走ってもらう時は少ないので、引数がない場合は、普通に走ってもらうこととします。

Python オブジェクト指向 入門

 

オーバーロードの実装例

この実装方法は、通常の関数でも使う方法です。

 

引数=値をとして、引数に値を設定します。

In[]

では、実装例を見てみましょう。単純に、引数2つを足算する関数を用意しました。

 

 

では3つのパターンに分けて使ってみます。

  • 1つ目は arg2 に値をいれていません。
    この場合は、func_samp で指定された arg2=0 が使われます。
  • 2つ目は arg2 に0を入れます。なので1つ目と同じ結果になります。
  • 3つ目は arg2 に1を入れており、arg2 を1に書き換えて実行します。

 

In[]


Out[]

 

Dog Classのrun()メソッドを変更

では、先ほどのDog Classの run() メソッドに speed 引数を追加してみます。

 

 

では、実際に使ってみます。

  • 1つ目は引数を指定しませんので、普通の速さで走ります。
  • 2つ目は引数を指定して、早く走ってもらいます。

In[]

Out[]

 

これで、チョコちゃんに速く走ってもらったりと速さを指示できるようになりました。

 

補足

この関数に初期値を入れる方法は、オブジェクト指向とは関係なく、普通の関数を宣言するときもよく使われるものですので、しっかり習得しておく必要があります。

 

また、Javaとかの多言語でいうオーバーロードとは微妙に違うところもありますが、Python ではこの方法で実現するんだという認識で良いです。

 

オブジェクト指向の特徴 「カプセル化」

Python オブジェクト指向 入門

カプセル化はオブジェクト指向の考え方そのものです。

 

カプセル化 は、変数をオブジェクトの内部に隠すことを言います。

 

そうすることで、カプセル化された自分以外のオブジェクトがメンバ変数=プロパティを参照できない様にできます。

 

プログラムを一つのオブジェクトとして、使いやすいものにしようという考え方ですが、以下の図を参照にしてください。

Python オブジェクト指向 入門

 

カプセル化が使い易い理由としては、以下の理由があります。

  • メソッドの機能などがわかりやすく、中身を知らなくても期待通りの処理が動く
  • 使うのに必要なプロパティ、メソッドのみ使え、他は隠されている状態

2つ目は前回のオブジェクト指向の構成のプロパティメソッドプライベート変数メソッドがしっかり設定されていることを指します。

 

Pythonでは色々なライブラリをimportして使用しますが、これらのコードを見ることなく、メソッドを実行するだけ期待する処理が実行されます。

 

これは、きちんとカプセル化されている状態であるということです。

 

カプセル化は2つ目のことがよく言われます。前回のオブジェクト指向の構成のプロパティ、メソッド、プライベート変数、プライベートメソッドがしっかり作られてて、必要な機能が公開されていたり、隠されている状態です。

 

プロパティはよくゲッター、セッターというものでアクセス制限などを行うことがありますので、そこについて深掘りしていきます。

ゲッター、セッター

プロパティの役割としては、その属性の値を取得(ゲット)、設定(セット)することでその値を使用します。

 

前回、プロパティは変数で取り扱っていましたが、普通の変数と同じ扱いで、取得(ゲット)、設定(セット)が可能ですが、この取得と設定を関数で実現することがあります。

 

この値を取得(ゲット)する関数をゲッター、設定(セット)する関数をセッターと言います。

 

ゲッター、セッターを使う理由

意図せず値が変更されたりとバグの原因になることがあります。そのため、ゲッター、セッターを経由させることで、以下のことが行えます。

  • 読み込みのみの制限
  • 値の妥当性チェック

以上などを行う場合に使うことがあります。

 

ゲッター、セッターの作り方

ゲッター、セッターは以下のような記述ルールで作成します。

In[]

 

ゲッターセッターには @property@プロパティ名.setter がついていますが、そういったものだというくらいの認識で良いです。

 

ゲッターを使ってみる

 

プロパティをnameのみにして、ゲッターを追加します。下記になります。違うところは、下記2つです。

  1. self._name とプライベート変数で宣言
  2. @property  …のゲッター部分が追加

In[]

実際に使ってみましょう。使い方は通常のClassと同じです。

 

インスタンスして、プロパティの name を表示してみましょう。

In[]

 

ここまでは、普通です。では、dog1.name の値を変更してみましょう。

 

 

エラーが出ます。これが、読み込みのみに制限されている状態です。

In[]

 

セッターを追加してみる

先ほどのセッターを追加して、書き込みもできるようにしましょう。

 

コードが以下になります。

In[]

これで、エラーが出ずに name の値を変更できるようになります。

In[]

Out[]

補足

このnameというプロパティを記述するだけで、毎回記述することを考えると大変だと思います。

 

プロパティの取り扱いを厳格にしたい場合のみゲッターセッターを追加してあげるくらいで良いと思います。

 

Githubとかで見るコードでも、使っている人が少ないように感じますが、出会ったときにはこのことだと理解していただければと思います。

 

Pythonでは色々なライブラリをimportして使用しますが、これらのコードを見ることなく、メソッドを実行するだけ期待する処理が実行されると思います。

 

これは、きちんとカプセル化されている状態であるということです。

 

まとめ|Pythonにおけるオブジェクト指向について解説【入門編その②】

Python オブジェクト指向 入門

以上で2回に渡り、Pythonのオブジェクト指向について解説しました。

 

オブジェクト指向の総まとめの演習として、以下の課題がありますので、是非チャレンジしてください。

関連記事
Python オブジェクト指向 練習問題 入門
【Python】オブジェクト指向の練習問題【診療支援アプリ作成】

続きを見る

 

 

関連記事 機械学習は挫折しやすい【挫折が多い原因と挫折回避の方法を教えます】

関連記事 無料あり:機械学習エンジニアの僕がおすすめするAI(機械学習)特化型プログラミングスクール3社

 


-python
-, , , ,

Copyright© Tommy blog  , 2020 All Rights Reserved.