【Python】レッスン5-04:プロパティを理解しよう

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

この記事で学べる知識:プロパティ

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

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

Pythonの「プロパティ」とは

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



前回の記事でクラス内のデータをカプセル化して外部からの直接アクセスを防ぐ方法と、安全にアクセスできるゲッターとセッターについて学びました。

今回はそのゲッターやセッターをさらに便利に扱える「プロパティ」について解説します。

プロパティを使うことで、コードをよりシンプルに、かつ直感的に記述できるようになります。

プロパティとは?

プロパティはPythonが提供する機能で、クラスの属性に対するアクセス方法をカプセル化しながら、外部からはあたかも通常の属性のようにアクセスできる仕組みです。

通常、ゲッターとセッターを使ってクラスの属性を操作しますが、プロパティを使うことで、これらのメソッドを明示的に呼び出す必要がなくなり、コードが洗練されます。

Pythonでは@propertyデコレーターを使用してプロパティを定義し、ゲッターとセッターをシンプルに扱います。

これによりユーザーは通常の属性のようにデータを操作できますが、実際には内部でメソッドが呼び出されて処理が行われます。

プロパティの使用例

次に、簡単な例を見てみましょう。Personクラスでは、名前の取得と設定をプロパティで制御しています。

class Person:
    def __init__(self, name):
        self._name = name  # 外部から直接アクセスされないように、アンダースコアを付けた属性を使用します

    # name属性のゲッター
    @property
    def name(self):
        return self._name

    # name属性のセッター
    @name.setter
    def name(self, value):
        # セッターでは、値が適切かどうかを確認できます
        if not isinstance(value, str):
            raise ValueError("名前は文字列でなければなりません")
        self._name = value

# 使用例
p = Person("Alice")
print(p.name)  # Aliceと表示される

p.name = "Bob"
print(p.name)  # Bobと表示される

# p.name = 123  # これはエラーを発生させます(名前は文字列でなければならないため)

この例では@propertyを使用してname属性にゲッターとセッターを定義しています。

こうすることでnameにアクセスする際にメソッドを呼び出す必要がなく、p.nameの形式で簡単にデータの取得と設定ができます。

また、セッターでは値の検証が行われ、不正な値が代入されないようになっています。

プロパティの利点

プロパティの主な利点は、属性のアクセス方法をカプセル化し、データの整合性を保ちながら柔軟に管理できる点です。

たとえば、将来的にデータの保存方法が変わったとしても、外部のコードを変更する必要はありません。

プロパティを使っている限り、内部の実装が変わっても外部のインターフェースは同じまま維持できます。

まとめ

Pythonのプロパティは、クラスのデータを安全に管理し、コードの可読性と保守性を向上させるための重要な機能です。

プロパティを適切に活用することで、クラスの内部データに対する直接的なアクセスを防ぎ、データの一貫性を保ちながら柔軟なプログラムを作成できます。

プロパティは、オブジェクト指向プログラミングにおけるカプセル化の重要な要素の一つですので、ぜひ使いこなせるようになりましょう。

練習問題5-4:名前と年齢を管理するクラスを作成しよう

クラスを使って名前と年齢を管理するプログラムを作成しましょう。

このプログラムでは名前と年齢を取得するためのゲッターメソッドと、値を設定するためのセッターメソッドを実装します。

プロパティを使用して、直接的に属性にアクセスする代わりにメソッドを通じてデータの取得や設定を行いましょう。

この問題の要件

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

  • クラスPersonを作成し、名前と年齢を属性として持つこと。
  • インスタンス変数_name_ageをカプセル化し、外部から直接アクセスできないようにすること。
  • プロパティnameageを使用して、名前と年齢を取得・設定できるようにすること。
  • name属性には文字列のみを設定できるようにし、文字列以外が設定された場合はエラーを発生させること。
  • age属性には0以上の整数のみを設定できるようにし、不正な値が設定された場合はエラーを発生させること。

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

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

名前: 太郎
年齢: 25歳
新しい名前: 次郎
新しい年齢: 30歳

この問題を解くヒント

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

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

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

1:Personクラスの定義
  □ コンストラクタ(init)の定義
  □ □ インスタンス変数_nameにnameの値を代入
  □ □ インスタンス変数_ageにageの値を代入
  □ nameプロパティのゲッターの定義
  □ □ name属性の値を返す
  □ nameプロパティのセッターの定義
  □ □ nameの値が文字列かどうかを判定し、エラーを発生させるか、インスタンス変数_nameに値を設定
  □ ageプロパティのゲッターの定義
  □ □ age属性の値を返す
  □ ageプロパティのセッターの定義
  □ □ ageの値が整数かつ0以上かどうかを判定し、エラーを発生させるか、インスタンス変数_ageに値を設定
1:Personクラスのインスタンスを作成し、変数personに代入
2:プロパティnameを使って名前を取得し、f文字列で「名前:」と出力
3:プロパティageを使って年齢を取得し、f文字列で「年齢:」と出力
4:プロパティnameを使って名前を「次郎」に設定
5:プロパティageを使って年齢を30に設定
6:プロパティnameを使って新しい名前を取得し、f文字列で「新しい名前:」と出力
7:プロパティageを使って新しい年齢を取得し、f文字列で「新しい年齢:」と出力

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

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

class Person:
    def __init__(self, name, age):
        """【穴埋め問題1】
        ここにインスタンス変数の初期化を行うコードを書いてください。
        """

    # name属性のゲッター
    @property
    def name(self):
        """このメソッドは、name属性を取得するためのゲッターです。"""
        return """【穴埋め問題2】
        ここにname属性の値を返すコードを書いてください。
        """

    # name属性のセッター
    @name.setter
    def name(self, value):
        """このメソッドは、name属性に新しい値を設定するためのセッターです。"""
        """【穴埋め問題3】
        ここにname属性の値を検証し、設定するコードを書いてください。
        """

    # age属性のゲッター
    @property
    def age(self):
        """このメソッドは、age属性を取得するためのゲッターです。"""
        return """【穴埋め問題4】
        ここにage属性の値を返すコードを書いてください。
        """

    # age属性のセッター
    @age.setter
    def age(self, value):
        """このメソッドは、age属性に新しい値を設定するためのセッターです。"""
        """【穴埋め問題5】
        ここにage属性の値を検証し、設定するコードを書いてください。
        """

# インスタンスの作成
"""【穴埋め問題6】
ここにPersonクラスのインスタンスを作成するコードを書いてください。
"""

# プロパティを使って名前と年齢を取得
print(f"名前: {person.name}")
print(f"年齢: {person.age}歳")

# プロパティを使って名前と年齢を設定
"""【穴埋め問題7】
ここにnameとage属性の値を設定するコードを書いてください。
"""

# 設定後の名前と年齢を表示
print(f"新しい名前: {person.name}")
print(f"新しい年齢: {person.age}歳")

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

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



解答例と解説

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

正解コードの例

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

class Person:
    def __init__(self, name, age):
        # インスタンス変数の初期化(カプセル化を考慮して、変数名にアンダースコアをつける)
        self._name = name
        self._age = age

    # name属性のゲッター
    @property
    def name(self):
        """このメソッドは、name属性を取得するためのゲッターです。"""
        return self._name

    # name属性のセッター
    @name.setter
    def name(self, value):
        """このメソッドは、name属性に新しい値を設定するためのセッターです。"""
        if not isinstance(value, str):
            raise ValueError("名前は文字列でなければなりません")
        self._name = value

    # age属性のゲッター
    @property
    def age(self):
        """このメソッドは、age属性を取得するためのゲッターです。"""
        return self._age

    # age属性のセッター
    @age.setter
    def age(self, value):
        """このメソッドは、age属性に新しい値を設定するためのセッターです。年齢は0以上の整数でなければなりません。"""
        if not isinstance(value, int) or value < 0:
            raise ValueError("年齢は0以上の整数でなければなりません")
        self._age = value

# インスタンスの作成
person = Person("太郎", 25)

# プロパティを使って名前と年齢を取得
print(f"名前: {person.name}")
print(f"年齢: {person.age}歳")

# プロパティを使って名前と年齢を設定
person.name = "次郎"
person.age = 30

# 設定後の名前と年齢を表示
print(f"新しい名前: {person.name}")
print(f"新しい年齢: {person.age}歳")

正解コードの解説

このPythonコードではプロパティを通じて、データを保護しつつ、アクセスや変更を安全に行う方法を紹介します。

クラス定義とコンストラクタ

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age

このコードは Person というクラスを定義しています。

__init__ という特別なメソッドは「コンストラクタ」と呼ばれ、オブジェクトが作られるときに自動的に実行されます。

ここではname(名前)と age(年齢)という2つの属性を初期化しています。

変数の名前の前にアンダースコア(_)をつけることで、これらの変数は直接アクセスされるべきでないことを示しています。

プロパティとゲッター

@property
def name(self):
    return self._name

@property はゲッターと呼ばれるメソッドを定義します。

このメソッドにより、name を直接 person.name のように呼び出して使うことができます。プロパティを使うことで、データの取得が安全に行えます。

ゲッターを使えば、_name というプライベートなデータにアクセスする際に不正な操作を防ぐことができます。

プロパティとセッター

@name.setter
def name(self, value):
    if not isinstance(value, str):
        raise ValueError("名前は文字列でなければなりません")
    self._name = value

@name.setter はセッターと呼ばれるメソッドです。

このメソッドはname を変更するときに使われます。セッターはデータを変更する際にバリデーション(検証)を追加することができるため、データの一貫性を保つのに役立ちます。

ここではname が文字列でなければエラーを出すようにしています。

ageのゲッターとセッター

@property
def age(self):
    return self._age

age 属性にもプロパティのゲッターを使って値を取得しています。

これによりperson.age といった簡単な方法で値を取得できます。

@age.setter
def age(self, value):
    if not isinstance(value, int) or value < 0:
        raise ValueError("年齢は0以上の整数でなければなりません")
    self._age = value

こちらは age のセッターです。

年齢が整数で0以上であることを確認してから値をセットしています。もし不正な値が入ると、エラーメッセージが表示されます。

このようにしてデータが常に正しい形で保持されることを保証します。

インスタンスの作成とプロパティの利用

person = Person("太郎", 25)

ここではPerson クラスのインスタンス(実体)を作成しています。

このときname に「太郎」、age に25という値を渡しています。

print(f"名前: {person.name}")
print(f"年齢: {person.age}歳")

プロパティを使ってnameage の値を取得し、それを表示しています。

プロパティのおかげで、変数に直接アクセスするのではなく、セッターやゲッターを通じて安全にデータを扱っています。

プロパティを使った値の変更

person.name = "次郎"
person.age = 30

このコードはセッターを使って name を「次郎」、age を30に変更しています。

このときセッターによってデータのバリデーションが行われ、適切な値が設定されます。

まとめ

このコードでは、クラス、コンストラクタ、プロパティ、ゲッター、セッターの基本的な使い方を学びました。

特にプロパティを使うことで、データの不正なアクセスや変更を防ぐと同時に、簡単にデータを取得・設定できる便利さを理解できたと思います。

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

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

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

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






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