【Python入門】カプセル化を使おう|ゲッターとセッターで内部データを守る方法【レッスン5-3】

ながみえ

一つ前の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を用いたアプリ開発

<<前のページ

【Python学習記事のアイキャッチ画像】Lesson5-2 メソッドの基本を理解しよう

Pythonの記事一覧

Python学習カテゴリの親ページ用アイキャッチ画像(テキスト&問題集)、記事一覧へのリンク案内

次のページ>>

【Python学習記事のアイキャッチ画像】Lesson5-4 プロパティを理解しよう

カプセル化とは?|クラス設計におけるデータ保護の基本

この章ではPythonにおける「カプセル化」の意味や使い方を学習します。必要ない方はここをクリックして練習問題へ飛びましょう。

【Python】勉強猫がノートパソコンを前にして学習を始める様子。記事内の学習スタート用イラスト

オブジェクト指向プログラミングの重要な概念の一つに「カプセル化」があります。

カプセル化はオブジェクトのデータ(属性)を外部から直接アクセスできないようにし、データの保護や管理を容易にするための手法です。

Pythonでは、変数やメソッドのアクセス制御を簡単に行うことができます。今回はこのカプセル化の概要について解説します。

カプセル化とは

カプセル化とはクラス内のデータ(属性)やメソッドを他のクラスや外部コードから見えないようにし、データの保護を行う手法です。

これにより外部から誤ってデータが変更されることを防ぎ、クラスの内部動作を隠蔽することができます。

Pythonでは属性の名前の前にアンダーバー(_)を付けることで、直接アクセスを避けるように促す「慣習的なプライベート変数」を定義できます。

またアンダーバーを2つ(__)にすることで、名前修飾を行い、さらに強力な保護を提供します。

アンダーバー1つ(慣習的プライベート)

アンダーバー1つ(_)を変数やメソッド名の前に付けると、その変数やメソッドは「慣習的にプライベート」と見なされます。

つまり外部からアクセスすることは可能ですが、アクセスを控えるように促すサインです。

class MyClass:
    def __init__(self, value):
        self._private_variable = value  # 慣習的にプライベートな属性

この例では_private_variableという属性はアンダーバー1つで定義されており、外部から直接アクセスすることが可能です。

しかし、可能な限りアクセスは避けるべき、という意味を込めてアンダーバーが1つついています。

アンダーバー2つ(名前修飾によるプライベート)

アンダーバー2つ(__)を使うことで、Pythonは名前修飾を行い、クラス外部からのアクセスをより厳しく制限します。

この方法はクラス内部での属性やメソッドを保護するために使用されます。

class MyClass:
    def __init__(self, value):
        self.__private_variable = value  # 名前修飾されたプライベートな属性

この例では__private_variableは名前修飾され、クラス外部から直接アクセスしようとするとエラーが発生します。

名前修飾により外部からのアクセスが制限され、より強力なカプセル化が実現されます。

ゲッターとセッター

カプセル化をすると属性に直接アクセスできなくなるため、外部からデータを取得したり変更したりするための「ゲッター」と「セッター」が必要になります。

これらは属性に対するアクセスや変更を制御し、データの整合性を保つために使用されます。

 ゲッターとセッターの使い方

ゲッターは属性の値を取得するためのメソッドで、セッターは属性の値を設定(変更)するためのメソッドです。

Pythonではゲッターとセッターを使って、カプセル化されたデータへのアクセスを適切に制御します。

class 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())

obj.set_value(-5)  # 不正な値を設定しようとすると警告が表示される

この例ではget_valueメソッドでプライベート変数の値を取得し、set_valueメソッドでその値を変更しています。

またセッター内でデータのバリデーションを行い、不正な値の設定を防いでいます。

まとめ

カプセル化はクラス内のデータを保護し、データの整合性を保つために重要な手法です。

Pythonにはアンダーバー1つを使った「慣習的プライベート」と、アンダーバー2つによる「名前修飾」があり、属性へのアクセスを制御することができます。

またゲッターとセッターを使うことで、データの取得や変更を安全に行うことが可能です。

これによりコードの可読性や保守性が向上し、バグの発生を防ぐことができます。

練習問題:クラスとカプセル化の基礎を学ぼう

【Python】勉強猫がノートパソコンを前にして学習を始める様子。記事内の学習スタート用イラスト

クラスを使って商品の名前と価格を管理するプログラムを作成しましょう。

クラスの初期化メソッドを使って、商品の名前と価格を設定し、プライベート属性を用いることでデータのカプセル化を実現します。

最後に商品価格を変更し、正しい価格管理ができていることを確認しましょう。

問題の詳細条件

以下の要件に従ってコードを完成させてください。

  • 商品を表現する「Product」クラスを作成し、名前と価格を初期化メソッドで設定すること。
  • 名前はプライベート属性 _name、価格はプライベート属性 _price として設定すること。
  • 商品名を取得する get_name() メソッドを定義し、名前を返すこと。
  • 商品の価格を取得する get_price() メソッドを定義し、価格を返すこと。
  • 商品の価格を変更する set_price() メソッドを定義し、価格が 0 以上の場合にのみ価格を更新すること。価格が0未満の場合は警告メッセージを表示すること。
  • 商品のインスタンスを作成し、名前と価格を設定すること。
  • 商品名と価格を表示し、価格の変更も確認すること。

ただし、以下のような実行結果となるコードを書くこと。

商品名: りんご
価格: 100円
新しい価格: 120円
価格は0より大きい値を設定してください。

【ヒント】難しいと感じる人だけ見よう

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

Q
ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。
(※下記の□はコード内のインデントを表しています)

1:クラスProductの定義
□ 初期化メソッド__init__の定義
□ □ 商品名をself._nameに代入
□ □ 価格をself._priceに代入
□ ゲッターメソッドget_nameの定義
□ □ 商品名を返す
□ ゲッターメソッドget_priceの定義
□ □ 価格を返す
□ セッターメソッドset_priceの定義
□ □ 価格が0より大きいか判定
□ □ □ 真の場合、self._priceに新しい価格を代入
□ □ □ 偽の場合、警告メッセージを表示
2:クラスProductのインスタンスproductを作成し、商品名「りんご」と価格100を設定
3:商品名と価格を取得し、f文字列で商品名を出力
4:商品名と価格を取得し、f文字列で価格を出力
5:セッターメソッドset_priceを使い、価格を120に設定
6:f文字列で新しい価格を出力
7:無効な価格-50を設定しようとするが、警告が表示される

Q
ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

# クラスの定義: 「商品」を表現するクラス
class Product:
    # 初期化メソッド (__init__) を使って、商品の名前と価格を設定
    def __init__(self, name, price):
        # アンダーバー1つの変数は「慣習的にプライベート」属性として扱われます
        """【穴埋め問題1】ここにnameとpriceをプライベート変数として設定するコードを書いてください。"""

    # 商品名を取得するためのゲッターメソッド
    def get_name(self):
        """【穴埋め問題2】ここに商品名を返すコードを書いてください。"""

    # 商品の価格を取得するためのゲッターメソッド
    def get_price(self):
        """【穴埋め問題3】ここに商品の価格を返すコードを書いてください。"""

    # 商品の価格を設定するためのセッターメソッド
    def set_price(self, price):
        # 価格は0以上でなければならないという条件を設定
        """【穴埋め問題4】ここに価格が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)  # この操作は失敗し、警告が表示される

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

問題の答え合わせと解説

この問題の一つの正解例とそのコードの解説を以下に示します。

一つの正解例

例えば以下のようなプログラムが考えられます。

Q
正解コード
# クラスの定義: 「商品」を表現するクラス
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)  # この操作は失敗し、警告が表示される

正解例の詳細解説

このコードは「商品」を表現するためのクラスを定義し、商品名や価格を管理する仕組みを提供しています。

このコードを通じてオブジェクト指向プログラミングの基本的な概念、特に「カプセル化」について学びます。

Q
詳細解説

クラスの定義

class Product:

ここではProductというクラスを定義しています。

クラスはオブジェクト(具体的には商品)の設計図のようなもので、商品ごとのデータや操作をまとめて管理できます。

初期化メソッド(コンストラクタ)

def __init__(self, name, price):
    self._name = name
    self._price = price

__init__メソッドはクラスのインスタンス(オブジェクト)が作成される際に自動的に呼び出され、オブジェクトの初期設定を行います。

ここでは商品の名前と価格を設定します。self._nameself._priceのようにアンダースコアが付いている変数は「慣習的にプライベート」とされ、クラスの外部から直接アクセスされないようにします。

カプセル化とゲッター・セッターメソッド

def get_name(self):
    return self._name
def get_price(self):
    return self._price
def set_price(self, price):
    if price > 0:
        self._price = price
    else:
        print("価格は0より大きい値を設定してください。")

「カプセル化」とは、データを外部から隠蔽し、データにアクセスする方法を制御することです。

この例では商品名や価格を直接操作するのではなく、get_name()get_price()メソッドを使ってデータにアクセスし、set_price()メソッドを使って価格を変更します。

こうすることで価格が0以下の場合には警告を表示するなど、データの不正な変更を防ぎ、データの一貫性を保つことができます。

インスタンスの作成とメソッドの利用

product = Product("りんご", 100)
print(f"商品名: {product.get_name()}")
print(f"価格: {product.get_price()}円")

上記のコードでは、Productクラスを使って「りんご」という商品を作成し、その名前や価格を取得して表示しています。

これによりオブジェクト指向の実際の使用方法を確認できます。

セッターメソッドの利用とバリデーション

product.set_price(120)
print(f"新しい価格: {product.get_price()}円")
product.set_price(-50)  # この操作は失敗し、警告が表示される

価格の変更はset_price()メソッドを使って行います。

このメソッド内では価格が0より大きいかどうかをチェックする「バリデーション」が行われており、不正な値を防いでいます。

このバリデーションも「カプセル化」により実現されるデータ保護の一例です。

まとめ

今回のコードを通じて、クラスの定義、初期化メソッド、そしてゲッターメソッドやセッターメソッドの使い方を学びました。

特に「カプセル化」により、データの隠蔽や制御がどのように行われているかを理解できたかと思います。

カプセル化はオブジェクト指向プログラミングの重要な概念であり、データの保護や管理を効率的に行うために不可欠な要素です。

これを理解することでより堅牢で安全なプログラムを作成できるようになるでしょう。

<<前のページ

【Python学習記事のアイキャッチ画像】Lesson5-2 メソッドの基本を理解しよう

Pythonの記事一覧

Python学習カテゴリの親ページ用アイキャッチ画像(テキスト&問題集)、記事一覧へのリンク案内

次のページ>>

【Python学習記事のアイキャッチ画像】Lesson5-4 プロパティを理解しよう

FAQ|Pythonカプセル化の基本

Q
Q1. カプセル化とはどんな場面で使うべきですか?

データを外部から直接変更されたくないときに使います。重要な変数を守り、予期しない変更を防ぐことでバグを減らし、コードの信頼性を高められます。

Q
Q2. ゲッターとセッターはいつ使えばいいですか?

外部から値の読み取りや変更を制御したいときに使います。例えば、値のチェックやログ記録など、単純な変数アクセスに追加処理が必要な場合に便利です。

Q
Q3. Pythonでは変数に「」や「_」をつける理由は?

変数名」は内部利用の目印、「_変数名」は名前の衝突を避けるための名前修飾(ネームマングリング)に使われます。カプセル化の一環としてよく用いられます。

質問用コンタクトフォーム

この記事はAIを用いて書いた記事です。

人間の目による確認も行っていますが、もし間違い等ありましたらご指摘頂けると大変助かります。

Python記事トップへ戻る
トップページへ戻る

記事URLをコピーしました