【Python】関数、メソッド、クラスの継承、属性(attribute)を再確認(基礎)
こんにちは、個人事業OguLinks代表の小倉(@jun_ogulinks)です。
職場でPythonの勉強を始めた方からクラスや属性に関していまいち理解できないという意見があったため記事にしようかと思います。
普段気に留めていないことを質問されて調べてみると知識の再確認や新しい発見があったりしますよね。
というわけで今回はPythonプログラミングのクラス関連について基礎のお話をしていきます。
こちらの記事では以下のことが分かるようになっています。
- 関数とメソッドの違い
- クラスの継承
- Pythonで同点判定を行うコード例
- 属性(attribute)
- 括弧()が必要なときと要らないときの区別
Pythonにおける関数とメソッドの違い
プログラミング自体が初めてで、Pythonの勉強を始めた方から以下のような質問を受けました。
下のようなコードを見つけたのですが、引数scoreをmax関数に代入しています(str関数も同様の形です)。
これはnum=score. max()と書いてはダメなんでしょうか?
score = [21,30,44,55,20]
num = max(score)
print('最高得点は' + str(num))
メソッドの勉強をしていた時、下のコードようにリスト型変数や辞書型変数の後ろにメソッドを付けるのを見ていたので、違いがイマイチ釈然としません。
stations=['神田','東京','新宿']
stations.append('渋谷')
print(stations)
stations={1:'神田', 2:'東京', 3:'新宿'}
for station in stations.values():
print('最寄り駅は' + station)
というような内容です。
あー、なるほど気持ちは非常に分かります。
確かに初学者には分かりにくい部分かもしれません。
自分もいまだに時々勢い余ってこの手のミスをすることがあります。
エラーが出て、「ああ、そうだった、うっかりうっかり・・・」となります。
しかし、出来ないものはできません。
何故なら、
- scoreというインスタンスオブジェクトにmax()というメソッドは用意されていないから。
- append()やvalues()はリスト型、辞書型にあらかじめ用意されて(組み込まれて)いるメソッドなので使える。
ただそれだけです。
組み込みで用意されていれば使えるし、用意されていないものは使えません。
と言っても初学者にはこの説明では伝わらないかもしれませんね。
これは関数とメソッドの違いが区別できていないから生じる問題なのではないかと思っています。
関数もメソッドもどちらもdef文で定義されたものなので、定義の仕方は同じですが、クラスの中で定義された関数を通常はメソッドと呼び、通常の関数と区別しています。
そして、関数とメソッドの呼び出し方の違いはインスタンス化して使うかどうかの違いと考えて良いような気がします。
つまり、下図のような感じです。
前述のコードの例で言えば、max()というのは関数なので 「max(引数)」 という形であれば使えます。
そして、append()はリスト型のメソッドなので「リスト変数.append(引数)」という形で使うことが出来ます。
上図の説明からすれば「リスト変数はインスタンス化してないじゃないか」と言われそうですが、リスト変数は作った時点で、内部で自動的にlistクラスが与えられているので動作としては同じことになります。
リストを継承して新しいクラスを定義
では、インスタンス変数.max() という形でどうしても使いたい場合にはどうしたらよいか?
結論から言うと、
自分で作ればいい
です(初学者には難しいですが・・・)。
書き方は色々あると思いますが、とりあえず質問者の意図する使用感に近いように書くとこんな感じになるのではないかと思います。
class scores(list): # リストクラスを継承して新しいクラスを定義
def __init__(self): # 初期化
super().__init__() # listの初期化を継承
def max(self): # リストの最大値を求めるメソッド
tmpMax = max(self)
return tmpMax
score = scores() # インスタンス化
score[:] = [21,30,44,55,21,21] # 要素を代入
print('全員のスコアは' + str(score) + 'です。')
print('最高得点は' + str(score.max()) + 'でした。')
# 出力結果 ------
# 全員のスコアは[21, 30, 44, 55, 21, 21]です。
# 最高得点は55でした。
まず、scores()という新しいクラスを定義します。
定義する際に、引数に list を記載することでlistクラスを継承することができます(簡単に言うと、自分で定義したクラスにリスト型変数の機能を盛り込むことができます)。
super()はなくても動きましたが念のため入れておいた方が良いかと思います。
リスト(list)や辞書型(dict)を継承するときにsuper()以外を使っているコードを見かけますが、Python3以降ではsuper()の方が良いのでは?と思ってしまうのですが、詳しい人が居たら教えて欲しいですね。
リスト(list)や辞書型(dict)の継承ではありませんが、似たようなものとして以下のようなものをstack overflowで見つけました。
(あまり内容を理解できていませんが)super()を使いなさいと言われています。
要素を代入する部分(score[:] = [21,30,44,55,21,21]のところ)ではこのような書き方でないとうまく行きません。
ここが個人的にはうまくなかったですね。
そして肝心なmax関数ですが、score.max()という形で使えるようになっていますね。
場合によってはこのようにメソッド化する方が使い勝手が良くなることもありますので、こういった書き方自体は覚えておいて損はないです。
また、その他の部分はだいたいリスト型変数と同じような使い方ができるようになっているかと思います。
Pythonで同点の判定を行うコードを作成
理解としてはここまでで良いですが、先程のコードでは面白みがないので、少しだけ機能を追加してみます。
同点の人がいるかどうか判定する TieJudgment() というメソッドを追加してみました(初学者にとっては一気にレベルが上がりますが、頑張って読み解いてみましょう)。
class scores(list): # リストクラスを継承して新しいクラスを定義
def __init__(self): # 初期化
super().__init__() # listの初期化を継承
def max(self): # リストの最大値を求めるメソッド
tmpMax = max(self)
return tmpMax
def TieJudgment(self): # 同点判定
for i in range(0, len(self)): # リストの各要素を順番に処理(繰り返し処理)
MatchIndexList = [] # 同点のインデックスを格納するための
# 空のリストを作成
count = self.count(self[i]) # 同じスコアが何個含まれているかを調べる
if count > 1: # 同点がある場合の処理
for k in range(0, len(self)): # 同点の人のインデックスを調べる
if i!=k: # 自分自身は検索しないようにする
if self[i]==self[k]: # 同点の人が居たら
MatchIndexList.append(k+1) # リストに追加
print(str(i+1) + ' 番目の人は ' + \ # バックスラッシュは改行の意味
','.join(map(str, MatchIndexList)) + ' 番目の人と同点です')
インデックスとはリストの中の何番目か、ということです。
ただし、Pythonはインデックスが0スタートなので注意しましょう。
このクラスをインスタンス化して実際に使ってみると、以下のようになります。
score = scores() # インスタンス化
score[:] = [21,30,44,55,21,21] # 要素を代入
print('全員のスコアは' + str(score) + 'です。')
print('最高得点は' + str(score.max()) + 'でした。')
score.TieJudgment() # 同点の人を調べる
# 出力結果 ------
# 全員のスコアは[21, 30, 44, 55, 21, 21]です。
# 最高得点は55でした。
# 1 番目の人は 5,6 番目の人と同点です
# 5 番目の人は 1,6 番目の人と同点です
# 6 番目の人は 1,5 番目の人と同点です
append() を使ってリストに要素を追加することも通常のリスト型変数と同じようにできます。
(ここでは55を追加してみました)
score.append(55) # リストの最後に要素を追加
print('全員のスコアは' + str(score) + 'です。')
print('最高得点は' + str(score.max()) + 'でした。')
score.TieJudgment()
# 出力結果 ------
# 全員のスコアは[21, 30, 44, 55, 21, 21, 55]です。 ← 最後に55が追加されています
# 最高得点は55でした。
# 1 番目の人は 5,6 番目の人と同点です
# 4 番目の人は 7 番目の人と同点です
# 5 番目の人は 1,6 番目の人と同点です
# 6 番目の人は 1,5 番目の人と同点です
# 7 番目の人は 4 番目の人と同点です ← 最後に追加した要素の同点結果
属性(attribute)呼び出しで括弧()が必要なときと要らないときの区別
インスタンス変数.メソッド() としたとき、ドットの後ろ側は属性(attribute)と呼ばれます。
インスタンス変数が持つ変数(クラス内で定義されている変数のこと)もアトリビュートと呼びます。
例えば以下のようなhogeという名前のクラスを定義したとします。
class hoge():
def __init__(self, a):
self.score = a
hogeObject = hoge(123)
print(hogeObject.score)
# 出力結果 ------
# 123
このとき、hogeObject.score の .score もアトリビュートです。
ここで気付くことが1つあります。
hogeObject.score の後ろに 括弧( ) がないことです。
どうやらこの括弧 ( ) の有無も初学者にはわかりずらい部分のようです。
簡単に言えば、
- .scoreのような変数の場合には()はいらない。
- 上記の .TieJudgment() のようなメソッドの場合は()が必要。
となります。
つまり、変数かメソッドかで()の有無が変わるということですね。
それほど難しくはないかと思います。
まとめ
初学者にとってクラスの理解はなかなか難しいと思います。
他の言語をやっていた人なら問題ないと思いますが、やはり最初はつまずきやすいポイントかと(自分もそうでした)。
しかし、コードを書くうえでクラスの継承などは結構使いますので、しっかり身に付けておきたいものです。
Pythonのプログラム・GUIアプリケーションを作って仕事の効率化をお手伝いします!
科研費等での支払いにも対応しており、国立・私立大学の先生から複数の受注実績があります。
対応可能かどうかも含めてお気軽に問い合わせページからご相談ください。
OguLInks公式サイトのトップページに簡単な事例紹介もありますのでご参照ください。
Twitterでお得情報をゲットしたい方はフォローいただけると幸いです。
(投稿一覧はこちらからどうぞ)