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

記事内に商品プロモーションを含む場合があります

この記事で学べる知識:カプセル化

この記事の練習問題で使用する知識:
レッスン1~4の知識クラスの定義と使用__init__メソッドメソッドの定義と使用カプセル化プロパティクラスの継承メソッドのオーバーライド静的メソッド

<<前の問題 問題集Top 次の問題>>

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つによる「名前修飾」があり、属性へのアクセスを制御することができます。

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

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

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

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

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

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

この問題の要件

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

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

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

*****↓↓正解コードの実行結果の例↓↓*****

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

この問題を解くヒント

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

ヒント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を設定しようとするが、警告が表示される

ヒント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)  # この操作は失敗し、警告が表示される

以上がこの問題の穴埋めコードです。

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



解答例と解説

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

正解コードの例

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

# クラスの定義: 「商品」を表現するクラス
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:

ここでは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より大きいかどうかをチェックする「バリデーション」が行われており、不正な値を防いでいます。

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

まとめ

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

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

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

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

<<前の問題 問題集Top 次の問題>>

この記事への質問・コメント

この記事を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






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