変数のアクセスを制限する
5.1 変数のアクセスを制限する
これまで習ってきたインスタンス変数は、インスタンスの生成と共に値が設定されましたが、インスタンスの生成後も、生成したインスタンスを介して値を書き換えることができます。
自由に書き換えができるということは柔軟にプログラミングを作成できる一方で、値を自由に書き換えられると、対象のインスタンス変数が使われているインスタンスメソッドの動作に影響がでたり、不具合が生じることもあります。
5.1.1 インスタンス変数の値を書き換えるプログラム
ではどのような不具合が生じるのか、以下のプログラムを例に確認していきましょう。
ソース・フォルダー: /Desktop/Python基礎講座
ファイル名: 第5章.ipynb
アクセスURL: http://localhost:8888/notebooks/Desktop/Python基礎講座/第5章.ipynb
class Computer: def __init__(self,name='Windows',memory='80'): self.name = name self.memory = memory def osMemory(self): print('【作成されたPCの内容】') print('・os名:',self.name) print('・メモリー:',self.memory,'GB') com1 = Computer() com1.memory = -30 com1.osMemory()
【作成されたPCの内容】 ・os名: Windows ・メモリー: -30 GB
このプログラムでは1~10行目でコンストラクタを定義する際に、インスタンス変数nameの初期値として「Windows」、インスタンスmemoryには「80」が設定されているため、インスタンスcom1を生成した段階では、com1のインスタンス変数には、これらの値が設定されています。
class Computer: def __init__(self,name='Windows',memory='80'): self.name = name self.memory = memory def osMemory(self): print('【作成されたPCの内容】') print('・os名:',self.name) print('・メモリー:',self.memory,'GB')
しかし13行目で、com1のインスタンス変数memoryに-30を代入したため、14行目でインスタンス変数の値を出力するメソッドosMemoryを実行すると、上記のような出力結果になります。
com1 = Computer() com1.memory = -30
本来パソコンはメモリのサイズをマイナス値にすることはないため、このような値が設定されると不具合が生じる可能性があります。
そこでそういった不具合を起こさせないためにも、既定のルールに沿ってクラスの設計を行う必要があります。
ではどのようなクラスの設計を行えればこのような不具合が起こらないのでしょうか?その仕組みについて次の項より1つずつ説明していきます。
5.1.2 頭文字に_(アンダースコア)をつける
前項の「5.1.1インスタンス変数の値を書き換えるプログラム」でメモリサイズにマイナスな値が入ってしまう誤りは何が原因だったでしょうか。その原因は変数に制限なくアクセスし、勝手な値(ここでは-30)を代入してしまったことにあるといえます。
Pythonでインスタンス変数を宣言する際には、このような間違いを起こさせないような仕組みを設ける必要があります。
ではそのような仕組みを実現するにはどうすれば良いのでしょうか。そのためにはまずはクラスの外から変数へ自由なアクセスを制限する方法を学ぶ必要があります。
Pythonでは、インスタンス変数のアクセス制限を行う時に、コンストラクタで定義する際に、インスタンス変数の頭文字に_(アンダースコア)を設けることでアクセス制限を示す慣習となってます。
書式:アクセス制限を示す
このようにインスタンス変数の頭文字にアンダースコアを付けることで、値へのアクセスや書き換えを制限することを示したことになります。
凡例
前節で示したサンプルのクラス「Computer」を例にします。
こうすることでインスタンス変数「memory」には参照しないよう示したことになりますが、示しただけでこのインスタンス変数に参照することはできます。
凡例
実行結果
メモリ: 80 メモリ: -30
この凡例のように、参照時に頭文字に_(アンダースコア)を付ければ、参照することも値を書き換えることもア可能です。
アンダースコアはあくまでアクセス制限を示したに過ぎず、プログラミング上の処理では、インスタンス変数の名前にアンダースコアが付いた (「memory」から「_memory」に名称が変わった)に過ぎないからです。
5.1.3 頭文字に_(アンダースコア)を2個つける
ではPythonのインスタンス変数におけるアクセス制限を実現するためにはどうすれば良いのでしょうか。コンストラクタ内で宣言する際に、インスタンス変数の頭文字に、_(アンダースコア)を2個付与させることで、コンストラクタ内で宣言したインスタンス変数にアクセス制限することができます。
書式:アクセス制限の実現
凡例
前節で示したサンプルのクラス「Computer」を例にします。
5.1.4 インスタンス変数をアクセス制限したプログラム
頭文字に_(アンダースコア)を2つけた状態のインスタンス変数に直接アクセスし、実際に変数にはアクセスできないことを確認します。
ソース・フォルダー: /Desktop/Python基礎講座
ファイル名: 第5章.ipynb
アクセスURL: http://localhost:8888/notebooks/Desktop/Python基礎講座/第5章.ipynb
class Computer: def __init__(self,name='Windows',memory='80'): self.name = name #アクセス制限を指定 self.__memory = memory #アクセス制限を指定 #インスタンスの生成 com1 = Computer() #インスタンス変数へアクセス print('OSの名前:',com1.name) print('メモリー:',com1.__memory)
実行結果
OSの名前: Windows --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-2-285c786946d6> in <module> 9 #インスタンス変数へアクセス 10 print('OSの名前:',com1.name) ---> 11 print('メモリー:',com1.__memory) AttributeError: 'Computer' object has no attribute '__memory'
解説
1~5行目でComputerクラスを定義しています。3~5行目でコンストラクタを定義しており、4、5行目ではインスタンス変数「name」と「__memory」が宣言されており、「__memory」に関しては行頭文字に_(アンダースコア)が2つ付与されているため、クラスの外からはアクセスできません。
9行目でComputerクラスからcom1という名称のインスタンスを生成しましたが、10、11行目でアクセス制限を指定したインスタンス変数にインスタンスを介してアクセスしようとしています。
実行結果を確認すると、インスタンスン変数のnameの値は出力されています。が、頭文字に_アンダースコアが2つ付与された「__memory」は出力されず、エラーになっていることから直接、アクセスできないことがわかります。
この凡例のアクセス制限に関する様子を図にまとめると以下の通りになります。
図 5.1.1: アクセス制限
この凡例におけるエラーメッセージの内容を見ると、「Computerクラスが「__ memory」という属性は所有していない」という内容になっております。
Computerクラスは実際には「__name」という属性が含まれているけど、クラスの外からアクセスするのを制限するために、このようなメッセージが表示されると言われており、アクセス制限かけたい変数を外部からは見えなくするため、オブジェクト指向のプログラミング言語では、データの隠蔽という呼び方をします。
保護したいクラス内の属性(インスタンス変数)を、自由にアクセスできなくするためにデータの隠蔽する仕組みをカプセル化と呼びます。カプセル化については次の項目で詳しく紹介します。
ポイント
- 頭文字に_(アンダースコア)を付与させたインスタンス変数はクラスの外から参照しないという慣習があるが、実際はアクセスできる。
- 頭文字に_(アンダースコア)を2つ付与させたインスタンス変数はクラスの外からアクセスできない。