【Python】レッスン5-03:カプセル化を理解しよう

一つ前のLessonではメソッドの基本について学習しました。
今回はカプセル化について見ていきましょう。
Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数とスコープ編
Lesson4:データ構造編
Lesson5:オブジェクト指向編
・Lesson5-1:クラスの基本を理解しよう
・Lesson5-2:メソッドの基本を理解しよう
・Lesson5-3:カプセル化を理解しよう ◁今回はココ
・Lesson5-4:プロパティを理解しよう
・Lesson5-5:クラスの継承を理解しよう
・Lesson5-6:メソッドのオーバーライドを理解しよう
・Lesson5-7:静的メソッドを理解しよう
・Lesson5-8:モジュールを使いこなそう
・Lesson5-9:抽象クラスを理解しよう
・Lesson5-10:ミックスインを理解しよう
・Lesson5-11:データクラスを理解しよう
・練習問題5-1:モンスター捕獲ゲームを作ろう
・練習問題5-2:モンスターとのバトルゲームを作ろう
次のステップ:Python基礎習得者にお勧めの道5選(実務or副業)
カプセル化入門|クラス設計でデータを守る基本
オブジェクト指向プログラミングの重要な概念の一つに「カプセル化」があります。
カプセル化はオブジェクトのデータ(属性)を外部から直接アクセスできないようにし、データの保護や管理を容易にするための手法です。
Pythonでは変数やメソッドのアクセス制御を簡単に行うことができます。今回はこのカプセル化の概要について解説します。
カプセル化とは?|データを外部から守る仕組み
カプセル化 とはクラス内の属性(変数やメソッド)を他のクラスや外部コードから見えないようにし、データの保護を行う手法です。
これにより外部から誤ってデータが変更されることを防ぎ、クラスの内部動作を隠蔽することができます。
Pythonでは属性の名前の前にアンダーバー( _ )を付けることで、直接アクセスを避けるように促す「慣習的なプライベート属性」を定義できます。
またアンダーバーを2つ( __ )にすることで、名前修飾を行い、さらに強力な保護を提供します。
慣習的なプライベート属性|先頭にアンダーバー1つ
アンダーバー1つ( _ )を変数やメソッド名の前に付けると、その属性は「慣習的にプライベート」と見なされます。
つまり外部からアクセスすることは可能ですが、アクセスを控えるように促すサインです。
例えば、以下のコードの _variable
という属性はアンダーバー1つで定義されており、外部から直接アクセスすることが可能です。
class MyClass: # MyClassクラスの定義 def __init__(self, value) # コンストラクタの定義 self._variable = value # 慣習的にプライベートな変数
しかし、可能な限りアクセスは避けるべき、という意味を込めてアンダーバーが1つついています。
名前修飾を使ったプライベート属性|アンダーバー2つ
アンダーバー2つ( __ )を使うことで、Pythonは名前修飾を行い、クラス外部からのアクセスをより厳しく制限します。
この方法はクラス内部での属性やメソッドを保護するために使用されます。
以下の例では __private_variable
は名前修飾され、クラス外部から直接アクセスしようとするとエラーが発生します。
class MyClass: def __init__(self, value): self.__private_variable = value # 名前修飾されたプライベートな属性
名前修飾により外部からのアクセスが制限され、より強力なカプセル化が実現されます。
- コラム:変数と属性の違い
-
コラム:変数と属性の違いと見分け方
Pythonを学んでいると、「変数」と「属性」という言葉が登場します。
どちらも「名前に値をひもづける」ものですが、役割と意味ははっきりと違います。
変数とは?|データを保管する箱
変数はあるスコープ(範囲)で値に名前をつける仕組みです。
最も身近なのは関数内のローカル変数や、ファイルの先頭にあるグローバル変数です。
x = 10 # x は変数 name = "Bob" # name も変数
これらは「どこに何があるか」を示す、プログラム全体の中で使える “名前札” のようなものです。
属性とは?|オブジェクト内部の名前付きデータ
属性は、オブジェクトが “自分専用に持っている名前付きのデータ” であり、”オブジェクトの一部” です。
アクセスするときは、
object.attribute
のようにドット記法を使います。class Person: def __init__(self, name): # このnameは変数(仮引数) self.name = name # self.nameは属性、右のnameは変数 p = Person("Alice") print(p.name) # p.nameは属性へのアクセス # pオブジェクトが持つnameという名前の属性にアクセスする # pオブジェクトが持つname = 3行目のself.name
上のコードでは、
self.name
はオブジェクトp
に属する属性。右側のname
は関数の引数として渡された変数です。このように、同じ文字列でも「文脈によって意味が変わる」点に注意しましょう。
変数と属性の違い一覧表
種類 意味 どこに属するか アクセス方法 例 変数 スコープ内で使う値を入れる箱 関数・モジュール x
,name
name = "Alice"
属性 オブジェクトが持つ名前付きの値 オブジェクト object.attribute
p.name
補足:属性は変数のように使えるが、変数とは別物
属性は「オブジェクトに紐づいた “専用の変数のようなもの” 」ともいえますが、変数とは扱いも保存場所も異なります。
属性はオブジェクトごとに保持され、Python内部では
__dict__
という仕組みで管理されます。
ゲッターとセッターとは?|安全に値を扱う方法
カプセル化をすると属性に直接アクセスできなくなるため、外部からデータを取得したり変更したりするための「ゲッター」と「セッター」が必要になります。
これらは属性に対するアクセスや変更を制御し、データの整合性を保つために使用されます。
ゲッターは属性の値を外から取得するためのメソッドで、セッターは属性の値を外から設定(変更)するためのメソッドです。
Pythonではゲッターとセッターを使って、カプセル化されたデータへのアクセスを適切に制御します。
class MyClass: # MyClassクラスの定義 def __init__(self, value): # コンストラクタ self.__private_variable = value # 名前修飾されたプライベート属性 def get_value(self): # ゲッターメソッドの定義 return self.__private_variable # 呼び出されたら、プライベート属性を返す def set_value(self, value): # セッターメソッドの定義 if value > 0: # 変更可能な値の条件 self.__private_variable = value # 値の変更 else: print("値は0より大きくなければなりません") obj = MyClass(10) print(obj.get_value()) # ゲッターメソッドを呼び出して値を取得 obj.set_value(20) # セッターメソッドを呼び出してを設定 print(obj.get_value())
この例ではget_value
メソッドでプライベート変数の値を取得し、set_value
メソッドでその値を変更しています。
またセッター内でデータのバリデーションを行い、不正な値の設定を防いでいます。
このように、クラス内にプライベートな属性を設定した場合は、その属性を変更する専用のメソッド(ゲッターとセッター)を用意しておくことが推奨されます。
ただし、Pythonには実はもっと簡単にカプセル化された属性にアクセスする方法もあります。
今回のゲッターとセッターの内容を理解した上で、次回の プロパティ の記事に進んでください。
まとめ|データを守り、必要なものだけを見せよう
この記事では、カプセル化の基本的な考え方と、その実装方法について学びました。
アンダーバーによる慣習的な非公開変数、名前修飾によるプライベート属性、そして値の安全な操作を可能にするゲッター・セッターの使い方を理解することで、クラスの内部データを保護しつつ、外部に必要なインターフェースだけを公開できるようになりました。
これにより、自分で設計したクラスに対して「どの情報を外部に見せるか」「どの処理を内部に隠すか」を意識できるようになり、より堅牢で拡張性の高いプログラムを書ける力がつきました。
この感覚を身につけたことで、オブジェクト指向設計をより深く楽しめるようになるはずです。自信を持って、次の学びへと進んでいきましょう。
- サイト改善アンケート|ご意見をお聞かせください(1分で終わります)
-
本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、みなさまの「プログラミングを学習する理由」などをアンケート形式でお伺いしています。1分だけ、ご協力いただけますと幸いです。
【Python】サイト改善アンケート
練習問題:カプセル化を使ってみよう
この記事で学習した「カプセル化」を復習する練習問題に挑戦しましょう。
問題|クラスとカプセル化の基礎を学ぼう
クラスを使って商品の名前と価格を管理するプログラムを作成しましょう。
クラスの初期化メソッドを使って、商品の名前と価格を設定し、プライベート属性を用いることでデータのカプセル化を実現します。
最後に商品価格を変更し、正しい価格管理ができていることを確認しましょう。
以下の要件に従ってコードを完成させてください。
- 商品を表現する「Product」クラスを作成し、名前と価格を初期化メソッドで設定すること。
- 名前はプライベート属性
_name
、価格はプライベート属性_price
として設定すること。 - 商品名を取得する
get_name()
メソッドを定義し、名前を返すこと。 - 商品の価格を取得する
get_price()
メソッドを定義し、価格を返すこと。 - 商品の価格を変更する
set_price()
メソッドを定義し、価格が 0 以上の場合にのみ価格を更新すること。価格が0未満の場合は警告メッセージを表示すること。 - 商品のインスタンスを作成し、名前と価格を設定すること。
- 商品名と価格を表示し、価格の変更も確認すること。
ただし、以下のような実行結果となるコードを書くこと。
商品名: りんご 価格: 100円 新しい価格: 120円 価格は0より大きい値を設定してください。
ヒント|難しいと感じる人だけ見よう
1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。
- ヒント1【コードの構成を見る】
-
正解のコードは上から順に以下のような構成となっています。
1:商品クラスを定義し、名前と価格をプライベート属性として持たせる
・クラスはclass
で定義し、わかりやすい名前をつけます
・初期化処理には__init__
メソッドを使います
・プライベート属性は名前の前にアンダーバーをつけます2:商品名と価格を外部から取得できるゲッターメソッドを定義
・メソッドはdef
で定義します
・値を返したいときはreturn
を使います
・プライベート属性にアクセスするときはself._属性名
を指定します3:商品の価格を変更できるセッターメソッドを定義
・値を更新したいときはself._属性名 = 値
を使います
・条件をチェックするにはif
文を使います
・無効な値の場合はprint()
で警告を出しましょう4:インスタンスを作成し、ゲッターとセッターを使って動作を確認
・クラスからオブジェクトを作るときは「クラス名(値, 値)」の形で書きます
・値を参照するときはゲッターメソッドを呼び出します
・値を変更するときはセッターメソッドを呼び出します
- ヒント2【穴埋め問題にする】
-
以下のコードをコピーし、コメントに従ってコードを完成させて下さい。
# クラスの定義: 「商品」を表現するクラス class Product: '''(穴埋め)初期化メソッドを定義し、商品名と価格をプライベート属性に設定する''' # 商品名を取得するためのゲッターメソッド def get_name(self): return '''(穴埋め)''' # 商品の価格を取得するためのゲッターメソッド def get_price(self): return '''(穴埋め)''' # 商品の価格を設定するためのセッターメソッド def set_price(self, price): # 価格は0以上でなければならないという条件を設定 if price > 0: self._price = '''(穴埋め)''' else: print("価格は0より大きい値を設定してください。") # 商品のインスタンスを作成 '''(穴埋め)Productクラスからインスタンスを作成するコードを書く''' # 商品名と価格を表示 print(f"商品名: {product.get_name()}") print(f"価格: {product.get_price()}円") # 価格を変更 product.set_price(120) print(f"新しい価格: {product.get_price()}円") # 無効な価格を設定しようとする product.set_price(-50) # この操作は失敗し、警告が表示される
このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。
解答例|ゲッターとセッターの体験プログラム
例えば以下のようなプログラムが考えられます。
- 正解コード
-
# クラスの定義: 「商品」を表現するクラス class Product: # 初期化メソッド (__init__) を使って、商品の名前と価格を設定 def __init__(self, name, price): # アンダーバー1つの変数は「慣習的にプライベート」属性として扱われます self._name = name # 商品名 self._price = price # 価格 # 商品名を取得するためのゲッターメソッド def get_name(self): return self._name # 商品の価格を取得するためのゲッターメソッド def get_price(self): return self._price # 商品の価格を設定するためのセッターメソッド def set_price(self, price): # 価格は0以上でなければならないという条件を設定 if price > 0: self._price = price else: print("価格は0より大きい値を設定してください。") # 商品のインスタンスを作成 product = Product("りんご", 100) # 商品名と価格を表示 print(f"商品名: {product.get_name()}") print(f"価格: {product.get_price()}円") # 価格を変更 product.set_price(120) print(f"新しい価格: {product.get_price()}円") # 無効な価格を設定しようとする product.set_price(-50) # この操作は失敗し、警告が表示される
解答例の解説|ゲッターとセッターの体験プログラムの考え方
解答例の詳細解説は以下の通りです。
- 詳細解説
-
商品クラスを定義
# クラスの定義: 「商品」を表現するクラス class Product: # 初期化メソッド (__init__) を使って、商品の名前と価格を設定 def __init__(self, name, price): # アンダーバー1つの変数は「慣習的にプライベート」属性として扱われます self._name = name # 商品名 self._price = price # 価格
この部分では、「商品」を表すクラスを作り、その基本情報として名前と価格を保存できるようにしています。
名前と価格はクラスの外から直接変更されないように、プライベート属性として定義されています。
これにより、クラスの利用者はメソッドを通じてしか値を扱えないようになり、安全にデータを管理できるようになります。2つのゲッターメソッドを定義
# 商品名を取得するためのゲッターメソッド def get_name(self): return self._name # 商品の価格を取得するためのゲッターメソッド def get_price(self): return self._price
この部分では、プライベート属性に直接アクセスできない代わりに、値を取り出すための方法を用意しています。
「名前を返すメソッド」と「価格を返すメソッド」をそれぞれ定義することで、クラスの利用者は安全に商品情報を取得できます。
このように、ゲッターはデータの外部公開を制御するために使われます。セッターメソッドを定義
# 商品の価格を設定するためのセッターメソッド def set_price(self, price): # 価格は0以上でなければならないという条件を設定 if price > 0: self._price = price else: print("価格は0より大きい値を設定してください。")
この部分では、商品の価格を変更するためのメソッドを定義しています。
ただし、どんな値でも受け入れるのではなく、0より大きい値だけを許可しています。もし無効な値が渡された場合は、価格を更新せずに警告メッセージを表示します。
このように、セッターを使うことで「データの変更ルール」をクラスの中に組み込むことができ、間違った値が設定されるのを防ぐことができます。インスタンス生成
# 商品のインスタンスを作成 product = Product("りんご", 100) # 商品名と価格を表示 print(f"商品名: {product.get_name()}") print(f"価格: {product.get_price()}円") # 価格を変更 product.set_price(120) print(f"新しい価格: {product.get_price()}円") # 無効な価格を設定しようとする product.set_price(-50) # この操作は失敗し、警告が表示される
この部分では、作成したクラスを実際に使ってみます。
まず「りんご」という商品を作り、初期価格を設定します。そのあとでゲッターを使って商品名と価格を表示し、セッターを使って価格を変更します。
さらに、不正な価格を設定したときに警告が出ることも確認します。
これにより、クラスの中で定義したルール(カプセル化やセッターによる値の制御)が、実際にどのように働くのかを体験できます。
カプセル化の疑問解消|FAQと用語のまとめ
初心者がつまずきやすいポイントをFAQとしてまとめ、またよく使う専門用語をわかりやすく整理しました。
理解を深めたいときや、ふと疑問に感じたときに役立ててください。
FAQ|カプセル化に関するよくある質問
- Q1. カプセル化とはどんな場面で使うべきですか?
-
データを外部から直接変更されたくないときに使います。重要な変数を守り、予期しない変更を防ぐことでバグを減らし、コードの信頼性を高められます。
- Q2. ゲッターとセッターはいつ使えばいいですか?
-
外部から値の読み取りや変更を制御したいときに使います。例えば、値のチェックやログ記録など、単純な変数アクセスに追加処理が必要な場合に便利です。
- Q3. Pythonでは変数に「」や「_」をつける理由は?
-
「変数名」は内部利用の目印、「_変数名」は名前の衝突を避けるための名前修飾(ネームマングリング)に使われます。カプセル化の一環としてよく用いられます。
Python用語集|カプセル化に関する用語一覧
今回の記事で出てきた用語・関数などを一覧で紹介します。
このサイトに出てくる 全てのPython用語をまとめた用語集 も活用してください。
Python用語 | 定義・使い方の概要 | 解説記事へのリンク |
---|---|---|
カプセル化 | オブジェクトの内部データや実装を隠蔽し、安全に操作するための仕組み。通常、アクセス制御やゲッター・セッターで実現される | 本記事 |
ゲッター | クラス内の非公開属性の値を外部から取得するためのメソッド | 本記事 |
セッター | クラス内の非公開属性の値を外部から設定するためのメソッド | 本記事 |
変数 | 値を一時的に保持し、後で再利用するために名前を付けたデータの入れ物 | Lesson1-2 |
属性 | クラスやインスタンスに属するデータ。self.名前 などで定義され、オブジェクトに紐づく | なし |
プロパティ | @property デコレーターなどで定義される、ゲッター・セッターを使った属性アクセスの制御手段 | Lesson5-4 |