Pythonを含むオブジェクト指向のプログラミング言語には、クラス内のメソッドや変数をクラスの外部から呼び出すことができないようアクセスを制限する方法が設けられています。この記事ではそのためのプライベートメソッドとプライベート変数について解説します。
目次
アクセス制限の方法
Pythonではクラス内の変数やメソッドにアクセス制限をかけるには次のように記述します。
class クラス名: def __メソッド名(self): 処理
このようにメソッド名や変数名の前にアンダーバーを2つ追加するとそれらはクラスの中でしか呼び出せなくなります。
クラスの中でしか呼び出せないメソッドをプライベートメソッド、変数をプライベート変数(またはプライベートプロパティ)と呼びます。
プライベートメソッド
では実際にプライベートメソッドを記述してみましょう。
次のコードには名前と攻撃力の変数を持ち、攻撃するモンスターの名前を表示するメソッドと、引数で渡した敵を攻撃したメッセージを表示をするメソッドを持つクラスが定義されています。
class Monster: def __init__(self, name, ad): self.name = name self.ad = ad def declaration(self): print(self.name + "の攻撃") def attack(self, enemy): print(self.name + "は" + enemy + "に" + str(self.ad) + "のダメージを与えた") death_knight = Monster("デス・ナイト", 100) death_knight.declaration() death_knight.attack("ガゼフ")
処理としてはこれで問題ないのですが、ダメージを与えたメッセージは、○○の攻撃!の後以外のタイミングでは使われたくないとします。
そこで○○の攻撃!という宣言の後でのみ呼び出せるように変更してみましょう。
まずはattackメソッドをプライベートメソッドに変更します。
前略 def __attack(self, enemy): # アンダーバーを2つ追加 print(self.name + "は" + enemy + "に" + str(self.ad) + "のダメージを与えた") death_knight = Monster("デス・ナイト", 100) death_knight.declaration () death_knight.__attack("ガゼフ") # アンダーバーを2つ追加
attackメソッドを定義しているメソッド名の前にアンダーバーを2つ追加し、attackメソッドを呼び出しているコードでも同様にアンダーバー2つを追加しました。
このコードを実行してみましょう。
Traceback (most recent call last): File "C:\Users\User\Desktop\python_lessons\test.py", line 14, in <module> death_knight.__attack("ガゼフ") # アンダーバーを2つ追加 AttributeError: 'Monster' object has no attribute '__attack'
attackメソッドはプライベートメソッドなので、クラスの外から呼び出そうとしている最後の行はエラーになります。
このようにアクセス制限をかけるとメソッド名を正しく記述してもクラスの外からは呼び出せなくなります。
プライベートメソッドはクラスの中からのみ参照できるので、declarationメソッドの中で呼び出してみましょう。
前略 def declaration(self): print(self.name + "の攻撃") self.__attack("ガゼフ") # attackメソッドの呼び出しを追加 def __attack(self, enemy): print(self.name + "は" + enemy + "に" + str(self.ad) + "のダメージを与えた") death_knight = Monster("デス・ナイト", 100) death_knight.declaration() # クラス外からの呼び出しを削除
これでdeclarationメソッドを呼び出した際に、その中でattackメソッドが呼び出されるようになりました。
実行してみましょう。
デス・ナイトの攻撃 デス・ナイトはガゼフに100のダメージを与えた
ちゃんと攻撃するモンスター名が宣言された後で攻撃時のメッセージが表示されています。
プライベート変数
次はプライベート変数です。これはプライベートプロパティと呼ばれることもあります。
先ほどのコードのself.ad変数をクラスの外から呼び出せないようにしてみましょう。
class Monster: def __init__(self, name, ad): self.name = name self.__ad = ad # アンダーバーを2つ追加 def declaration(self): print(self.name + "の攻撃") self.__attack("ガゼフ") def __attack(self, enemy): print(self.name + "は" + enemy + "に" + str(self.__ad) + "のダメージを与えた") # アンダーバーを2つ追加 death_knight = Monster("デス・ナイト", 100) death_knight.declaration()
ad変数を定義している部分と呼び出している部分すべてにアンダーバーを2つ追加しました。
試しに実行してみましょう。
デス・ナイトの攻撃 デス・ナイトはガゼフに100のダメージを与えた
問題なく実行できました。
ただ、肝心なのはこのad変数がちゃんとクラスの外から呼び出せなくなっているかどうかです。
そこで最後の行に次のコードを追記してテストしてみましょう。
print(death_knight.__ad) # クラス外から呼び出し
実行結果は次のようになります。
Traceback (most recent call last): File "C:\Users\User\Desktop\python_lessons\test.py", line 15, in <module> print(death_knight.__ad) # クラス外から呼び出し AttributeError: 'Monster' object has no attribute '__ad'
ちゃんと最後の行でエラーが出ています。このようにメソッド名や変数名の前にアンダーバー2つを追加するとクラスの外から呼び出せないようにできます。
ただPythonには他のオブジェクト指向言語にあるようなprivateやprotectedなどの仕組みはなく、完全なプライベートメソッドや変数を実現することはできません。
上記の方法もある特殊な仕組みを使っているだけで、実は次のような書き方をすることで呼び出せてしまいます。
_クラス名__メソッド名または変数名
実際に先ほど作成したプライベート変数にアクセスしてみましょう。
先ほどエラーとなった最後の行を次のコードに変更してみましょう。
print(death_knight._Monster__ad) # クラス外から呼び出し
実行結果は次のようになります。
デス・ナイトの攻撃 デス・ナイトはガゼフに100のダメージを与えた 100
プライベート変数なのにクラス外から呼び出すことができてしまいました。
このようにPythonではクラス外からの呼び出しを厳密に禁止する方法はありません。ただこういった記述をすることでそのままにしておくよりはクラス外から呼び出しづらくなります。
実際の業務では呼び出してほしくないメソッドや変数を定義する際はアンダーバーを2つ付け、他の人のコードでも先頭にアンダーバーが付いているメソッドや変数にはクラスの外からアクセスしないよう注意しましょう。