はじめに
こんにちは。孔子の80代目子孫兼アプリケーションサービス部の孔です。前回の記事ではClassの定義方法、__init__
、そしてself
についてみてみました。今回は前回に引き続き、Classの使い方をあれこれを見てみましょう。
今回紹介する概念はアクセス修飾子とクラスメソッドになります。
アクセス修飾子とは…の前に、まずはカプセル化とは
アクセス修飾子の話をする前に、まずは「カプセル化」とは何かを説明します。カプセル化とは、「外部に詳細のロジックなどを公開せずに隠蔽すること」を指します。前回作ったのTable
クラスを見てみましょう。
import time class Table: # テーブルごとの注文に関するオブジェクト def __init__(self, table_num: int): # テーブル番号, 注文した料理, 滞在時間のデータを作る self.table_num = table_num self.order = [] self.start_time = time.time() def add_order(self, order: list): self.order.extend(order) return None def get_stay_time(self): return time.time() - self.start_time def billing(self): amount = self.__calculate_amount() return amount def __calculate_amount(self): menu = Menu.get_menu() amount = 0 for ordered in self.order: amount += menu[ordered] return amount class Menu: # メニューに関するオブジェクト @classmethod def get_menu(cls): menu = { "beef": 1000, "chicken": 800, "pork": 900 } return menu
この中で、billing
メソッドは「お客様が注文した合計金額を返す」処理をしています。現実世界を考えてみましょう。ある店員がお会計のときに、「10番テーブルのお客様が注文した合計金額を知りたい!」となったときに、合計金額を取得する方法は2つあります。
Menu
という別のクラスの中にget_menu
というメソッドがある。そこから各メニューの金額を取得する。メニューはdict型で実装されているので、get_menu
からreturnされたデータに注文されたメニューをキーとして渡し、それぞれのメニューの金額を確認する。確認が終わったら金額を全部足す。その値が合計金額となる。取得完了。billing
メソッドを実行する。取得完了。
どちらの方法で合計金額を取得している店員さんが嬉しいかは一目瞭然です。このように、合計金額を取得するためのメソッドが実装されていれば、その裏のロジックはどのようになっているのか知らなくてもとりあえず「テーブルの注文の合計金額を知りたければbilling
メソッドを実行すること」だけ覚えておけば良いわけです。
このように、裏のロジックを外に出さず、複雑な処理を隠蔽することを「カプセル化」と言います。そしてそのカプセル化を実現するために利用されるのが「アクセス修飾子(Access Modifier)」となります。
アクセス修飾子(Access Modifier)とは
アクセス修飾子とは、「ある属性やメソッドがどこからアクセス可能かを制御するもの」になります。先ほど話したbilling
と__calculate_amount
ですが、店員はbilling
だけ知っていればよくて、__calculate_amount
メソッドは知らなくても良いわけですね。なのでbilling
は外の世界(店員がアクセスできる世界)に公開して、__calculate_amount
は外の世界からは見えないところに隠蔽したいです。
billing
のように、どこからでもアクセス可能なメソッドや属性をpublic
といい、__calculate_amount
など、外には後悔しないで隠蔽したいものをprivate
と言います。(また、protected
というものもありますが、後ほど説明します)
public
とprivate
の大きな違いは、「隠蔽されているかどうか」になります。そのため、private
なメソッドや属性は外からは直接呼び出せるようにしたくないですね。その実装をする際に使用するのがアンダースコアになります。アンダースコアが先頭に二つ付いた属性やメソッドはprivate
になり、外からはアクセスできないようになります。以下のコードをみてください。
class Test: def __init__(self, public_prop, private_prop): self.public_prop = public_prop self.__private_prop = private_prop def get_pricate_prop(self): return self.__private_prop test = Test("public", "private") test.public_prop # "public"。アクセス可能 test.get_pricate_prop() # "private"。get_pricate_propはpublicなメソッドなので、アクセス可能 test.__private_prop # AttributeError: 'Test' object has no attribute '__private_prop':そんな属性ないよ!と言われる
簡潔に整理すると以下になります。
アクセス修飾子 | アクセス可能範囲 | Pythonでの実装方法 |
---|---|---|
public | どこからでもアクセス可能 | 先頭にアンダースコアをつけない |
private | クラスの中からしかアクセスできない | 先頭にアンダースコアを2つ付ける(__method_name , __prop_name など) |
protected | 該当クラス及び継承クラスからしかアクセスできない (ただし、Pythonにはprotectedの概念がないため、どこからでもアクセス可能です) |
先頭にアンダースコアを1つ付ける |
余談ですが、private
で使用するアンダースコア2つのことを、ダンダー(dunder)と言います。例えば、「ダンダー関数」とは、アンダースコア2つ(__)から始まる関数を言います。ダンダーは「Double Underscore」を略して「Dunder」という名前になりました。
クラスメソッド(Class Method)とは
今までメソッドを使うときは、インスタンスを作成してからメソッドを呼び出していました。しかし、ものによってはインスタンスを作成しなくても良いケースがあります。まさにメニューを返す処理がそうですね。テーブル注文コードを再度召喚します。
import time class Table: (省略) class Menu: # メニューに関するオブジェクト @classmethod def get_menu(cls): menu = { "beef": 1000, "chicken": 800, "pork": 900 } return menu
インスタンスを作る理由の一つは、インスタンスごとに異なるデータを保持させることができることからです。その観点から、Menu
クラスのget_menu
メソッドは、インスタンスごとに違う値を返すことはないですね。だとしたら、インスタンスを生成してからメソッドを呼び出さなくても良さそうです。このようなときに使用するのがクラスメソッドになります。
クラスメソッドを定義する方法は、コードのように関数の定義の1行上に@classmethod
を追加するだけで良いです。クラスメソッドの第一引数には、インスタンスでselfにインスタンス自身が入るように、クラス自信が入ります。これもself同様、class
やkurasu
などなんでも良いですが、慣習的にcls
が使われています。
最後に
次回は、デザインパターンを学ぶ上で必須知識となる、「継承」と「オーバーライド」を紹介します。それでは、次回もお楽しみに!
シリーズ一覧
- 第1章:デザインパターンを学ぶための基礎知識