「オブジェクト指向プログラミング」について具体的なイメージから使い方まで知りたいです。
これから、オブジェクト指向型プログラミングとはどの様なものか、具体的に2部構成で学んでいきます。
この記事ではPythonにおけるオブジェクト指向の前半である基本的な項目について学習します。
本記事の学習目標
- オブジェクト指向という考え方を学ぶ
- オブジェクト指向の構成要素を細分化して学ぶ
- オブジェクト指向を利用するメリットについて学ぶ
では早速、見ていきましょう。
オブジェクト指向とは
まず、プログラミングにはいくつか種類があることを押さえておきましょう。
プログラミングの種類
- オブジェクト指向プログラミング:オブジェクト=モノを組み立てて作り上げていくプログラミング
- 手続き型プログラミング:単調なルールにのっとったコードで動くプログラミング
- 関数型プログラミング:関数を使って動くプログラミング
オブジェクト指向型プログラミングには、Python以外の他の言語では、RubyやPHP, Java, Java Script があります。
オブジェクト指向とは「プログラムのコーディングの考え方、ルール」を意味します。
「オブジェクト指向」と言われる言葉のうち、オブジェクトとは モノの「単位」で扱います。
例えば、オブジェクトを犬として考えてみましょう。
オブジェクトとは【具体例を用いて解説】
犬について考えると、犬が持つデータには、「名前」、「犬種」、「体重」などがありますが、これをオブジェクトの【属性】と言います。
また、犬は「座る」、「寝る」、「食べる」などの行動をとります。
これをオブジェクトの【振る舞い】と言います。
オブジェクト指向の基本的な考えは、オブジェクト=犬がこの、【属性】、【振る舞い】を持つという点です。
このオブジェクトの属性や振る舞いをを実際のプログラムのコードにしていきます。
オブジェクトの認識番号について
オブジェクトの属性、振る舞いをコードにしていく前に、オブジェクトには認識番号がある、ということを理解しておきましょう。
オブジェクトのIDの確認方法
世界であるヒトがたった一人しかいない様に、Pythonにおいてもオブジェクトはたった一つしかありません。
たった一つであることを確認するためには、オブジェクトのID(認識番号)を調べれば分かります。
オブジェクトのIDの値を知るためには、id()
を使用します。引数の ()
には、オブジェクトを代入します。
実際にIDを知るためのコードを書いてみましょう。
Pythonでは、数字であれ文字であれ、同値のデータであればIDが同じになります。
1 2 3 4 5 6 7 | >>> tommy = "tommyblog" >>> id(tommy) 4373712520 >>> tommy2 ="tommybolg" >>> id(tommy2) 4373712520 |
この様に tommy
とtommy2
では記載の仕方が異なりますが、データは"tommyblog
" であるために id が同じになります。
しかし、シーケンスのタプルの場合にはオブジェクトが同じ内容であったとしても、Id が異なることに注意してください。
※ タプルについては以下の記事を参照ください。
【Python】タプルの使い方【基本から応用まで】
続きを見る
1 2 3 4 5 6 | >>> tommy1 = (1, 2, 3, 4) >>> tommy2 = (1, 2, 3, 4) >>> id(tommy1) 4373298856 >>> id(tommy2) 4373253240 |
この様に、タプルであればidが異なっているのが分かります。 ここで、tommy1
とtommy2
をセットに入れてみます。
1 2 3 | >>> tommy = {tommy1, tommy2} >>> print(tommy) {(1, 2, 3, 4)} |
すると、tommy1
とtommy2
は重複データとして判断されてしまい、tommy1
と tommy2
をセットにしても1つのデータしか入っていない事が分かります。
これは tommy1
と tommy2
のデータが重複しているためです。
すなわち、データの内容とidはそれぞれ異なる概念であることを理解しましょう。
異なるIDのオブジェクトをセットに代入しても重複されません。 セットはIDで重複と判断しないのが非常に重要です。
オブジェクトのハッシュ値の確認方法
tommy1
と tommy2
は異なるidを持ちますが、セットにすると重複データとして判断されてしまいました。
このようにセットはIDでオブジェクトの内容を判断していませんでした。
セットはIDとは "異なる方法" でオブジェクトを判断しているのです。
それではどの様にして、セットは重複を判断するのでしょうか。
セットがオブジェクトの値が重複しているかどうかは、 ハッシュ値 で判断します。
セットでtommy1
と tommy2
が重複データと判断された事とは逆の現象が、ハッシュ値によって起こり得ます。
オブジェクトが違っていても、オブジェクトの中身のデータが同じものであればハッシュ値は同値になる事があります 。
ここで、先程の tommy1
, tommy2
についてハッシュ値を調べてみましょう。 ハッシュ値を調べるためには hash()
で可能です。
1 2 3 4 5 | >>> hash(tommy1) 485696759010151909 >>> hash(tommy2) 485696759010151909 |
この様に tommy1
とtommy2
ではIDが異なっていても、ハッシュ値が同じであることが分かりました。
すなわち、セットで tommy1
, tommy2
が重複していたのは、tommy1
とtommy2
のハッシュ値が同じであったからなのです。
「==
はデータが同じ」、「is
はハッシュ値が同じ」
==
と is
の違いについてまとめておきます。
==
はハッシュ値が同じかどうか、!=
はハッシュ値が異なるかどうかを調べる演算子です。
一方でis
はIDが同じかどうか、is not
はIDが同値ではないかを調べる演算子です。 この2点の違いに注意してまとめておきましょう。
同値 | 同値ではない | |
ハッシュ値 | == | != |
ID | is | is not |
先程のtommy1
, tommy2
についてみてみましょう。
1 2 3 4 5 6 7 8 | >>> tommy1 == tommy2 True >>> tommy1 is not tommy2 True >>> tommy1 is tommy2 False |
オブジェクト指向プログラミングの特徴
オブジェクトの認識番号に関する理解が深まったところで、オブジェクトの周囲の単語のまとめておきます。
今まで出てきた、オブジェクト、属性、振る舞いは下記のような言い方をします。
オブジェクト指向プログラミングの特徴
- オブジェクト:クラス(class) を持つ
- 属性:プロパティ(property) を持つ
- 振る舞い:メソッド(method) を持つ
- オブジェクトとオブジェクトの間に関係性を持たせる
- 各オブジェクトは独自の識別番号IDを持つ
※ 属性=プロパティのことを「オブジェクトのメンバ変数」と言うこともあります。
また、厳密には属性はクラス変数、インスタンス変数、プロパティなどに区別されますが、分かりやすさのため、プロパティを広義に属性として進めます。
これらは非常に重要な単語になりますので、是非覚えておきましょう。
これから、クラス、プロパティ、メソッドについて実際にプログラムのコードを書いていきます。
オブジェクト:クラス(class)
classの宣言は下記のようにします。
In[]
1 | class クラス名(): |
まず、Dog
というクラスを作成してみましょう。
宣言だけだと、エラーになりますので、pass文で「何も処理しないよ!」ということを指定しておきます。
In[]
1 2 3 | # オブジェクト = クラス(Class) class Dog(): pass |
これで、Dog
の Class
が作成できました。
実行しても、何も出力されませんが、Dog
の Class
が宣言されたことになります。
属性:プロパティ(property)
次に、プロパティを作っていきます。
プロパティといってもClassの中で宣言する、ただのClasの属性としての変数です。
name
(犬の名前) , breed
(犬種) , weight
(体重)を作ってみます。
In[]
1 2 3 4 5 6 7 8 9 10 11 | # オブジェクト = クラス(Class) class Dog(): #【属性】= プロパティ(property) # 名前 ポチ、タマとか name = "" # 犬種 ブルドック、ポメラニアンとか breed = "" # 体重[kg] weight = 0 |
実行しても何も出力されませんが、Dog Class
に name
, breed
, weight
を追加できました。
先ほどは処理を持たない Dog Class
のため、エラーを避けるため処理がないことは正常だよという意味で pass
を追記しましたが、プロパティの処理が追加されたので、pass
は不要になります。
振る舞い:メソッド(method)
最後に 振る舞い:メソッド(method)を宣言します。
これもClass内に宣言する関数という点を除けば、通常の関数とほとんど同じです。
通常の関数と違う点は、引数に必ず self
を入れる点です。self
については後で説明をいたします。
Class内で宣言する関数は必ずselfを入れましょう!
下記が、メソッド method
の宣言ルールです。
In[]
1 2 | def 関数名(self, 引数1, 引数2, ..., 引数n): 何らかの処理 |
実際に、sit
(座る)、 sleep
(寝る)、eat
(食べる)の状態をmethodを利用して作っていきましょう。
method
には何も処理コードを記述していませんが、例えば、ゲームに登場してくる犬が何かしら振る舞いをしてくれる処理イメージで置き換えてください。
In[]
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 | # オブジェクト = クラス(Class) class Dog(): #【属性】= プロパティ(property) # --- 省略 --- #【振る舞い】= メソッド(method) # 座る def sit(self): # 座る動作の処理コード print(self.name, '座る') # 寝る def sleep(self): # 寝る動作の処理コード print(self.name, '寝る') # 食べる def eat(self): # 食べる動作の処理コード print(self.name, '食べる') |
これで、Dog Classを宣言して、属性とメソッドを実装できました。
Class のコードまとめ
今までのDog Classをまとめると下記になります。
In[]
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 | # オブジェクト = クラス(Class) class Dog(): #【属性】= プロパティ(property) # 名前 ポチ、タマとか name = "" # 犬種 ブルドック、ポメラニアンとか breed = "" # 体重[kg] weight = 0 #【振る舞い】= メソッド(method) # 座る def sit(self): # 座る動作の処理コード print(self.name, '座る') # 寝る def sleep(self): # 寝る動作の処理コード print(self.name, '寝る') # 食べる def eat(self): # 食べる動作の処理コード print(self.name, '食べる') |
Class とは
Classには設計書のような役割を持つと言われています。
さすがに、設計書から現実の犬は作れませんが、プログラムで書かれる犬であれば作れるわけです。
実際にプログラムで犬を作ってみましょう。
dog1
, dog2
のように犬のClassから、いくつもの種類の犬を作ることができます。
この犬のClassの設計書から、dog1
(レオくん),dog2
(チョコちゃん)のように実体化することをインスタンスと言います。
クラスとインスタンスの関係
- オブジェクト(対象)はクラスという設計書によって生成される。
- 同じクラスから別々の個性を持ったオブジェクトを生成する事ができる。
- 「イヌ」のクラスの「レオちゃん」であった場合↓
- 「レオちゃん」は「イヌ」のクラスの「インスタンス」
- 「クラス」は分類枠であり、「インスタンス」は個体。
Classの使い方
作ったClassはインスタンスして使うということがわかったので、実際にDog Classを使っていきましょう。
インスタンス
インスタンスはClass(オブジェクト)は設計書から実体化することでした。
Classは実体化することで使えるので、まずインスタンス化する必要があります。
インスタンスは、変数名=クラス名()で宣言します。
In[]
1 | dog1 = Dog() |
プロパティの値を変更
プロパティへの値を変更するには変数名.プロパティ名 = 新しい値とします。
In[]
1 2 3 | dog1.name = 'レオ' dog1.breed = 'ブルドック' dog1.weight = 15 |
dog1.name
となっているのは dog1
の name
でもあるので、感覚的にもわかりやすいかと。
プロパティから値を取得
プロパティの値の取得は変数名.プロパティ名をそのまま使います。
print文で表示してみましょう。
In[]
1 | print(dog1.name, dog1.breed, dog1.weight) |
Out[]
1 | レオ ブルドック 15 |
無事、設定した値が表示されました。
メソッドを使う
メソッドを使う場合は変数名.メソッド名()となります。
メソッドの場合、プロパティと違い、カッコ書きが必要になりますので、注意ください。
In[]
1 | dog1.sleep() |
Out[]
1 | レオ 寝る |
無事メソッドも動きました。これもプロパティ同様にdog1がsleepとなるので感覚的にわかりやすいと思います。
print文で表示しているだけですが、ここで実際にゲームの犬が寝るといった処理を実行してみましょう。
dog2で復習
dog2
を同様にインスタンスして、プロパティの設定と表示、メソッドを使ってみてください。
下記、実行例です。
In[]
1 2 3 4 5 6 7 | # Dogオブジェクトからを dog2をインスタンス(実体化) dog2 = Dog() dog2.name = 'チョコ' dog2.breed = 'ビーグル' dog2.weight = 10 print(dog2.name, dog2.breed, dog2.weight) dog2.sit() |
実行結果は下記になります。
Out[]
1 2 | チョコ ビーグル 10 チョコ 座る |
うまく動いたでしょうか。
画像は処理イメージです。
クラスのインスタンス化の練習
また、別にクラスのインスタンス化の練習をしていきましょう。
まず Dog
クラスのインスタンスメソッドを作ってみましょう。
1 2 3 4 5 6 7 | class Dog: # property voice = "wan!" # method def scream(self): print(self.voice) |
まず、このコードをファイル dogs.py として保存しましょう。
それから新しくuse_dogs.py というファイルを作成しましょう。
作成したら以下の記述を行なってください。実行すると下の「wan!
」が表示されます。
1 2 3 | from dogs import Dog pochi = dog() pochi.scream() |
1 2 | ec2-user:~/environment $ python use_dogs.py wan! |
最初にまず Dog
のクラスのインスタンスメソッドをインポートしましょう。
(※ インスタンスメソッドとは、self.メソッド名(引数)
の様な書き方のこのタイプのメソッドです。)
そのために、最初に from ファイル名 import クラス名
と記述してください。
今回であれば、from dogs import Dog
としましょう。
この様に記載することで、dogs.py に定義されたクラスであるクラスの Dog
をimportする事が出来ます。
次に、インスタンスを作成してみましょう。
ポイント
self.メソッド名(引数)
このタイプのメソッドは インスタンスメソッド といいました。
1 | pochi = Dog() |
インスタンス = クラス名()
と記述します。
この様に記載する事で、初期の何も情報のない真っさらなクラスDog()
をインスタンスである変数 pochi
に収納する事ができます。
(ここで、初期である理由は Dog()
の ()
に何も入れていないからです。)
これだけでは何も起こらないため、scream()
メソッドを実行しましょう。
オブジェクトの変数名.メソッド名(引数)
すなわち、pochi.scream()
と記述します。
selfとは
ここで、Class
(オブジェクト)のmethod
(振る舞い)の記述に self
というものがあります。
これから、self
について解説していきます。
In[]
1 2 3 | def sit(self): # 座る動作の処理コード print(self.name, '座る') |
英語の self
は自分,自身という意味です。
Python の self
も同じような意味で、インスタンス(実体)そのもののことを指します。
具体的には、dog1
である犬のレオちゃんにとって、self
は dog1
を意味しますので、self.name
は dog1.name
と同じことを指し、「レオ」となります。
最後に、もう一度クラス class
の復習です。
イヌにしても、ネコにしても、ヒトにしても、カピバラにしても、あるクラスを定義するには class
を使って定義します。
while
や if
, for,
でもあったようにインデントを入れて改行している部分がクラスにかかっている(共有範囲)となります。
復讐として、最後にクラスを定義してみましょう。
1 2 3 4 5 6 7 | class Dog: # property voice = "wan!" # method def scream(self): print(self.voice) |
この Dog
クラスで、wan
というプロパティと、scream
というメソッドを定義しました。
ここまでは理解できましたでしょうか。
ポイント
インスタンスを生成する際に、引数()
に self
を入れないのは、self
がインスタンス自身のためです。
クラスの定義の中で self
を使用する理由は、クラスが定義されている時点ではインスタンスが生成されていないので、インスタンスの代わりに self
を使用して定義をしています。
オブジェクト指向の構成要素を見てみよう
一般的なClass(オブジェクト)は以下の構成要素で成り立っています。
- コンストラクタ
- プロパティ
- メソッド
- プライベート変数
- プライベートメソッド
すべてが必要な訳ではなく、簡単なClassであれば、プライベート変数やメソッドがない時もあります。
プライベート変数、メソッドは、対象のClassを使う人に使って欲しくない変数やメソッドがあるときに実装します。
オブジェクト指向の構成要素を使用したコード例
Class(オブジェクト)を構成する要素を全て組み込んだコードの一例として、以下の様になります。
それでは、Classを構成する5つの要素について、詳しく見ていきましょう。
Class(オブジェクト)を構成する5つの要素
- その1:コンストラクタ
- その2:プロパティ
- その3:メソッド
- その4:プライベート関数
- その5;プライベートメソッド
また、これまで記述したコードと比較して、オブジェクト指向を利用するとどの様に変更していくのか、実際にみていきましょう。
変更点は以下3点になります。
- プロパティをコンストラクタで初期化
- プライベート変数を追加
- プライベートメソッドを追加
具体的にこれから行っていくコードの変更点は以下の様にまとめることが出来ます。
それではこれらについて、一つずつ見ていきましょう。
コンストラクタ(初期化)とは
コンストラクタとはClass(オブジェクト)をインスタンス(実体)にするときに、初期化する処理です。
具体的には、今まで記述したコードでは、インスタンスした後に、プロパティの値を変更していました。
In[]
1 2 3 4 | dog2 = Dog() # インスタンス dog2.name = 'チョコ' dog2.breed = 'ビーグル' dog2.weight = 10 |
これでも十分コードは動きますが、下記のようにインスタンス時にカッコ()
の中に値を与えて、その値でプロパティの値を初期化する場合が多いです。
Out[]
1 | dog1 = Dog(name='レオ', breed='ブルドック', weight=15) #インスタンス&初期化 |
それでは、コンストラクタを作成していきましょう。
コンストラクタを作成してみる
コンストラクタはClassをインスタンス(実体)にするときに、初期化する処理です。
以下の決まり文句で宣言されます。
In[]
1 | def __init__(self, 変数1, 変数2, ..., 変数n): |
ここでは、インスタンス(実体)時に name
, breed
, weight
を与えたいと思います。
以下がコードになります。
In[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # オブジェクト = クラス(Class) class Dog(): # 1.コンストラクタ def __init__(self, name, breed, weight): # 2.プロパティ(property) # 名前 ポチ、タマとか self.name = name # 犬種 ブルドック、ポメラニアンとか self.breed = breed # 体重[kg] self.weight = weight # 3.メソッド(method) # --- 省略 --- |
ここで、いくつかポイントがあります。
def __init__
(self
, 初期化したい変数を入れる)↑
1番最初のメソッド名(引数)
の引数にはself
を入れるself
は、自分自身のオブジェクトを指すキーワードです↑
すなわち、プロパティにself
をつけるself.変数名
やself.メソッド名()
といった書き方をすることで、自身が持つオブジェクトを持つプロパティを参照したり、メソッドの実行が可能self.変数名
やself.メソッド名()
はプロパティやメソッドの実行を行うために必要なキーワード- プロパティ名と引数の変数名は同じにする(慣習的なもの)
これで、引数で与えられる、name
、breed
、weight
はそのまま初期値として、self.name
, self.breed
, self.weight
に設定され、初期化ができます。
重要なことなので、おさらいですが
self
がつくとメンバ変数(プロパティ)self
がついていなければ、外部から代入された変数
プロパティやメソッド、オブジェクトの用語が不安な方は以下の記事を参照にしてください。すぐに把握できます。
【Python】オブジェクトとメソッドについて【初心者向け】
続きを見る
メソッドを記載する時には、self.メソッド名(引数)
という形で記述してください。
self.メソッド名(引数)
の様な書き方のこのタイプのメソッドは インスタンスメソッド といいます。
コンストラクタを追加した後の使い方
コンストラクタを追加して、初期値の変数を設定したので、インスタンス時の使い方も変わります。
前回は、dog1 = Dog()
でしたが、このカッコ書きに初期化する値を入れます。
In[]
1 2 | dog1 = Dog(name='レオ', breed='ブルドック', weight=15) print(dog1.name, dog1.breed, dog1.weight) |
実行結果は下記になります。
Out[]
1 | レオ ブルドック 15 |
わかりやすくするために、コンストラクタで設定した変数名を使っていますが、省略しても使うことができます。
この場合は、def __init__(self, name, breed, weight):
の宣言通りの順番で値を与える必要があります。
In[]
1 2 | dog1 = Dog('レオ','ブルドック',15) print(dog1.name, dog1.breed, dog1.weight) |
プライベート変数、メソッドについて
プライベート変数、メソッドはそのClass内部のみから使うことを目的とします。
クラスを設計すると、インスタンスしたオブジェクトから使る事が可能なプロパティ、メソッドもあります。
一方で、使うことの出来ない変数やメソッドを宣言することもあります。これらは、プライベート変数、メソッドとして宣言します。
下記イメージ図では、_eaten
がプライベート変数になります。
Classの外部、内部から使うとはどういことか、もう少し詳しくみていきましょう。
Class外部から使う
Classを外部から使うとはインスタンスした dog1
から dog1.name
や dog1.sit()
のような、今まで使用していた方法です。
In[]
1 2 | print(dog1.name) # プロパティを使う dog1.sit() # メソッドを使う |
Class内部から使う
プライベート変数、メソッドのように内部から使うものは、dog.nameのように外部から使わせたくないものです。
今回作成する、プライベート変数「_eaten
」、プライベートメソッド「_output_data
」はname
や sit()
と同じようには使ってはいけません。
In[]
1 2 3 | # 使ってはダメ!! print(dog1._eaten) # プライベート変数は使ってはいけない dog1._output_data() # プライベートメソッドは使ってはいけない |
内部で使うとは、Dog Classの中でのみ使うということです。
例えば、プライベート変数 _eaten
はDog Class内の eat()
のメソッド内で使います。
In[]
1 2 3 4 5 6 7 8 9 10 | # オブジェクト = クラス(Class) class Dog(): #--- 省略 --- # 食べる def eat(self): # 食べる動作の処理コード print(self.name, '食べる') self._eaten = True # ----プライベート変数を使うのは、Dog Class内のみ --- |
これらのプライベート変数、メソッドは使うとバグになってプログラムが正常に動かなくなるとか、外部から使う必要がないものを対象にします。
プライベート変数の実装
プライベート変数の_eaten
を追加します。
プライベート変数には目印としてアンダーバー をつけます。
追加は、__init__
のコンストラクタの中に self._eaten
として宣言し、もう一つはメソッド eat()
を実行したときに、self._eaten = True
として実行しています。
下記のコードを見てみましょう。
In[]
1 2 3 4 5 | # 1.コンストラクタ def __init__(self, name, breed, weight): # ---省略--- # 4.プライベート変数 self._eaten = False #追加箇所 |
1 2 3 4 5 6 | # 食べる def eat(self): # 食べる動作の処理コード print(self.name, '食べる') # プライベート変数を使う self._eaten = True #追加箇所 |
追加したら、以下コードを実行してみます。
In[]
1 2 3 4 5 6 | # dog1をインスタンス dog1 = Dog(name='レオ', breed='ブルドック', weight=15) # eatメソッド実行 dog1.eat() # _eatenの値を確認 print(dog1._eaten) |
Out[]
1 2 | レオ 食べる True |
外部から使って欲しくないとはいえ、print(dog1._eaten)
のように使えて、値がTrueになっているのが確認できますね。
動作確認のため、外部から使っていますが、先ほど言った通り、ダメな方法になります。
外部から使わせない方法として、アンダーバーを二つつける方法があります。
これを使っても良いのですが、マングリングという同じ名前の衝突を避けるための機能で、実際に使用している方はほとんど見かけません。
よく使われるコーディングルールではアンダーバー 1つですので1つにしておきましょう。
プライベートメソッドの実装
次に、プライベートメソッドを実装します。
インスタンス時に、name
などを表示する _output_data
を実装します。
追加箇所は2箇所になります。eat()
メソッドの下に、他のメソッドと同じように下記の _output_data()
を追加します。
1 2 3 4 5 | """ 5.プライベートメソッド """ def _output_data(self): print(f'私は{dog1.name},{dog1.breed}で体重は{dog1.weight}kg') |
そして、コンストラクタの __init__
の最後に self._output_data()
を追加します。
1 2 3 4 5 6 | def __init__(self, name, breed, weight): -- 省略 --- # 4.プライベート変数 self._eaten = False self._output_data() # 追加箇所 |
で は実際に動作確認をしましょう。ただ、Dog Classをインスタンスするだけです。
1 | dog2 = Dog(name='チョコ', breed='ビーグル', weight=10) |
_output_data()
メソッドが動いて、レオくんの情報が表示されました。
コード例
最後に、5つの要素を全部入れたコードが下記になります。
In[]
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 | # オブジェクト = クラス(Class) class Dog(): # 1.コンストラクタ def __init__(self, name, breed, weight): # 2.プロパティ(property) # 名前 ポチ、タマとか self.name = name # 犬種 ブルドック、ポメラニアンとか self.breed = breed # 体重[kg] self.weight = weight # 4.プライベート変数 self._eaten = False self._output_data() # 3.メソッド(method) # 座る def sit(self): # 座る動作の処理コード print(self.name, '座る') # 寝る def sleep(self): # 寝る動作の処理コード print(self.name, '寝る') # 食べる def eat(self): # 食べる動作の処理コード print(self.name, '食べる') self._eaten = True # 5.プライベートメソッド def _output_data(self): print(f'私は{dog1.name},{dog1.breed}で体重は{dog1.weight}kg') # インスタンス dog1 = Dog(name='レオ', breed='ブルドック', weight=15) dog1.sit() |
プロパティ(メンバ変数)
プロパティは「属性」のことです。
以下の内容を記載して dogs.py
というpython のファイルを作成してみてください。
1 2 3 4 5 6 7 | class Dog: # property voice = "wan!" # method def scream(self): print(self.voice) |
dogs.py
の中でメンバ変数の定義部分は、こちらです。(メンバ変数とはプロパティのことです)
voice = "wan!"
今までの変数の定義と同じです。クラスの中で、このように記述すると、メンバ変数を1つ用意したことになります。
ここでは voice
というメンバ変数を定義しました。つまり、イヌの「鳴き声」です。
このように定義した"wan!
" というメンバ変数を クラスメンバ変数 と言います。
メソッド
メソッドは「振る舞い」のことです。
オブジェクトをどう動かしたいかをメソッドに記述します。
メソッドの使い方は「オブジェクト
」に「 .
」 をつけて、メソッドを実行」します。たったそれだけです。
例えば、 Tommy
.職業
であれば 産婦人科医
と出力される様なものです。
例えば、下記コードでは、インスタンスしたdog2(チョコちゃん)に座って欲しいと指示します。
1 2 | dog2 = Dog(name='チョコ', breed='ビーグル', weight=10) dog2.sit() |
プライベート変数、メソッド
プライベート変数、メソッドはそのClass内部のみから使うことを目的とします。
今回のコードのプライベート変数は「_eaten
」、メソッドは「_output_data
」としています。
Class外部から使う
プライベート変数、メソッドのように内部から使うものは、dog.name
のように外部から使わせたくないものです。
_eaten
はメソッド eat()
を実行したときに、eat()
の内部で使っています。
1 2 3 4 5 6 | # 食べる def eat(self): # 食べる動作の処理コード print(self.name, '食べる') self._eaten = True |
_output_data
はコンストラクタ時にその内部で使っています。
1 2 3 4 5 6 7 8 9 | def __init__(self, name, breed, weight): -- 省略 --- self._output_data() """ 5.プライベートメソッド """ def _output_data(self): print(f'私は{dog1.name},{dog1.breed}で体重は{dog1.weight}kg') |
ココがポイント
Pythonの場合、アンダーバー1つつけたものは実際にはアクセスできてしまいますが、他の人が見たときに、外部から使って欲しくないものだとわかります。
オブジェクト指向を利用するメリット
オブジェクト指向を利用するメリットとしては、「再利用」がしやすく、「可読性」よくなり結果、バグを減らせる効果などが期待できます。
コメント
他にも、継承、多態性、カプセル化などの考え方もありますが、次回の記事で説明します。
可読性
例えば、犬をオブジェクト指向でなくコードにすると下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | dog1 = {'name': "レオ", 'breed': "ブルドック", 'weight': 15,} dog2 = {'name': "チョコ", 'breed': "ビーグル", 'weight': 10,} # 座る def dog_sit(dog): # 座る動作の処理コード print(dog['name'], '座る') # 寝る def dog_sleep(dog): # 寝る動作の処理コード print(dog['name'], '寝る') # 食べる def dog_eat(dog): # 食べる動作の処理コード print(dog['name'], '食べる') print(dog1['name'], dog1['breed'], dog1['weight']) dog_sit(dog1) |
これくらいのコード量なら、むしろスッキリしてコードが見やすいと思われるかもしれません。
しかし、犬だけでなく、猫、人が出てきたとしたら、スッキリしにくくなります。
dog1
, cat2
, human100
を座らせると考えたときに、
1 2 3 | dog_sit(dog1) cat_sit(cat2) human_sit(human100) |
とするより、下記のコードの方が直感的で、読みやすコードになっているのではないでしょうか。
1 2 3 | dog1.sit() cat2.sit() human100.sit() |
再利用
再利用しやすいというのは、設計書であるClassをインスタンス(実体)することで何度でも再利用できるということを意味します。
インスタンスの中には、プロパティ(属性)とメソッド(振る舞い)が含まれるのでとても扱いやすくなります。
1 2 3 4 5 6 | dog1 = Dog(name='レオ', breed='ブルドック', weight=15) dog2 = Dog(name='チョコ', breed='ビーグル', weight=10) .... dog100 = Dog(name='ポチ', breed='柴犬', weight=12) dog1.sit() |
まとめ|オブジェクト指向プログラミング【入門編その①】
今回学習した内容の復習です。 オブジェクト指向の基本的な項目
- オブジェクト:クラス(class)
- 属性:プロパティ(property)
- 振る舞い:メソッド(method)
コーディングルール PEP8 について確認しましょう。 これはPythonのオブジェクト指向でのコーディングルールです。 またコーディングルールで気にする点は、下記です。
- クラス名:最初大文字
- 関数、変数名:小文字とアンダーバー
- プライベート変数、メソッド:最初にアンダーバーをつける
下記リンクを参照して下さい。
» PEP8: Pythonのコーディング規約 今回は以上とします。
また、「self の箇所でつまづいてしまった」というコメントが多かったですので、ここで重要なポイントをまとめておきます。
今回のまとめ:その①
- 「クラス」とは、オブジェクトを作成するためのテンプレート。
- 「クラス」には実態はなく、「クラス」はデータ構造を設計する分類枠。
- 「クラス」に沿った形で「インスタンス」は生成される。そこで生成された「インスタンス」は固体となる。
- 重要なことは、「インスタンス」が最初に生成された際には初期化を行う。
- 「インスタンス」とは「クラス」の定義に従って属を持つオブジェクトである。
- 「プロパティ」には属性を与える。「 . 」を付ける。
- クラス内に「メソッド」を定義した際には、仮引数に必ず「self」を付ける。
self.変数名
や self.メソッド名()
など。。。
- self がつくとメンバ変数(プロパティ)となる。
- self がついていなければ、外部から代入された変数となる。[/box]この違いを押さえておきましょう。
今回のまとめ:その②
メソッドの書式はある程度決まっていますので、以下の書式と流れをおさえておきましょう。
1 2 3 4 | # クラスとメソッドの書式 class クラス名: def __init__(self, 引数・・・): #初期化処置を行なっている |
- "
__init__
" は「コンストラクタ」の定義の方法である。 - オブジェクト生成時は必ず
__init__
にself
を加えることで初期設定を行う。 - クラスに沿ったオブジェクトを生成する。(同じクラスから別々のオブジェクトを生成する事が可能)
例として以下のものをあげておきます。
1 2 3 4 5 6 | # クラスとメソッドの作り方の一例 class Tommy_Class def f(self, value): print(f'self: {self}') print(f'value: {value}') |
まずはこの書式に慣れてください。
今回のテーマはPython学習の中でも一つのヤマですので、頑張って乗り切りましょう。