2. カプセル化
オブジェクト指向プログラミングの三大特徴は、カプセル化、継承、ポリモーフィズムです。
2.1 カプセル化の理論
2.1.1 カプセル化とは何か
オブジェクト指向プログラミングにおいて、オブジェクトの属性(データ)と振る舞い(メソッド)をオブジェクト内部に設計し、オブジェクト内のデータが外部から意図せずアクセスまたは変更されることを防ぐことを指します。
# Pythonでのカプセル化の例
class Student:
def __init__(self, name, chinese_score, math_score):
self.name = name
self.__chinese_score = chinese_score # 「プライベート」属性として定義
self.__math_score = math_score
def print_total_score(self):
print(f"{self.name}さんの総合得点は: {self.__chinese_score + self.__math_score}")
def print_average_score(self):
print(f"{self.name}さんの平均点は: {(self.__chinese_score + self.__math_score) / 2.0}")
2.1.2 なぜカプセル化が必要か
クラス内で定義された変数が、他のクラスからいつでもアクセス・変更できてしまうと、データの整合性が保てず、セキュリティ上のリスクが生じます。カプセル化は、データを保護し、意図しない操作から守るための重要な手法です。
2.1.3 カプセル化の設計規則
- メンバー属性を「プライベート」に修飾します。Pythonでは、名前を「__」で始めることで名前修飾(name mangling)が行われ、外部からの直接アクセスが困難になります。
- プライベート属性は、そのクラス内の他のメンバーからはアクセスできますが、他のクラスからは直接アクセスできません。
- プライベート属性に直接アクセスすることはできません。
- 間接的にアクセスするために、ゲッター(getter)とセッター(setter)メソッドを提供します。
2.1.4 まとめ
- カプセル化とは?
オブジェクトの属性と振る舞いをオブジェクト内部に設計し、データが意図せずアクセスされるのを防ぐこと。 - なぜカプセル化が必要か?
クラス内の変数が他のクラスからいつでもアクセス・変更されると、データの安全性が損なわれ、不安全です。 - どのようにカプセル化するか?
メンバー変数を「__」で始めることでプライベートに修飾します。 - カプセル化後、どのようにデータにアクセスするか?
対応する変数のゲッターとセッター(get/set)メソッドを提供します。
2.2 selfキーワード
2.2.1 selfとは何か
selfは、メソッド内で使用される変数で、現在のメソッドが属するオブジェクト(インスタンス)を表します。Javaのthisキーワードと同様の役割を果たします。
2.2.2 selfの応用シーン
selfは、メンバー変数とローカル変数の名前が衝突した場合に、どちらを指しているかを明確にするために使用されます。
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def check_pass(self, passing_score):
if self.score >= passing_score:
print(f"おめでとうございます、 Harvardに合格し、頂点に立つことができました!")
else:
print("申し訳ありませんが、不合格です。")
2.2.3 メンバー変数とローカル変数の違い
| 違い | メンバー変数 | ローカル変数 |
|---|---|---|
| クラス内の位置 | クラス内、メソッドの外 | 主にメソッド内(引数や単独で定義) |
| 初期化値 | デフォルト値があり、初期化は必須ではない | デフォルト値がなく、使用前に必ず代入が必要 |
| メモリ位置 | ヒープメモリ | スタックメモリ |
| スコープ | オブジェクト全体 | 属するブロック内 |
| ライフサイクル | オブジェクトと共存亡 | メソッドの呼び出し時に生成され、終了時に消滅 |
2.2.4 selfの実行原理
メソッドが呼び出されるとき、Pythonインタープリタは自動的にそのメソッドを呼び出したオブジェクトを第一引数(self)として渡します。これにより、メソッド内でそのオブジェクトの状態(メンバー変数)にアクセスできるようになります。
2.2.5 まとめ
- selfキーワードとは?
メソッド内で使用され、現在のメソッドを呼び出したオブジェクトを表す変数です。どのオブジェクトがメソッドを呼び出したかによって、selfが指すオブジェクトが変わります。 - selfキーワードの主な用途は?
オブジェクトのメンバー変数とメソッド内のローカル変数の名前が同じ場合のアクセス衝突を解決するために使用されます。 - メンバー変数とローカル変数の違い
クラス内の位置、初期化値、メモリ位置、スコープ、ライフサイクル。
2.3 コンストラクタ
2.3.1 知識理解
コンストラクタは、オブジェクトを作成するために使用される特殊なメソッドです。以下の特徴があります。
- メソッド名はクラス名と同じである必要があります。
- 戻り値の型は指定せず、voidも使用しません。
- オブジェクトが作成される際に自動的に呼び出されます(例:
student = Student())。 - クラスにコンストラクタが定義されていない場合、Pythonはデフォルトの無引数コンストラクタを自動的に生成します。
応用シーン
- オブジェクトを作成する際に、メンバー変数(属性)の初期化を同時に行う。
- 例:
student = Student("张三", 12)
注意点
- クラスにコンストラクタが定義されていない場合、Pythonはデフォルトの無引数コンストラクタを自動生成します。
- クラスに引数付きコンストラクタが定義されると、Pythonは自動的に無引数コンストラクタを生成しません。無引数コンストラクタが必要な場合は、手動で定義する必要があります。
2.3.2 まとめ
- 書式
メソッド名はクラス名と一致し、戻り値の型は指定しません。オブジェクト作成時に自動的に呼び出されます。クラスにコンストラクタがない場合、Pythonはデフォルトの無引数コンストラクタを生成します。class Student: def __init__(self): # ... - 応用シーン
全引数コンストラクタを使用して、オブジェクト作成時に変数に値を代入できます。 - 注意点
クラスにコンストラクタがない場合、Pythonはデフォルトの無引数コンストラクタを生成します。引数付きコンストラクタがある場合、Pythonは無引数コンストラクタを自動生成しません。必要な場合は手動で作成してください。
2.4 演習ケース
2.4.1 ケース: 「カプセル化」の思想で学生クラスを定義し、コンストラクタを定義してください。
「学生管理システム」用の「学生クラス(Student)」を設計します。
- メンバー属性:学籍番号(id)、氏名(name)、性別(sex)、年齢(age)
- メンバーメソッド:学生情報の表示(print)、学習(study)
# 完全なコード
class Student:
def __init__(self, student_id, full_name, gender, current_age):
self.__student_id = student_id # プライベート属性
self.__full_name = full_name
self.__gender = gender
self.__current_age = current_age
# ゲッターとセッター
def get_student_id(self):
return self.__student_id
def set_student_id(self, student_id):
self.__student_id = student_id
def get_full_name(self):
return self.__full_name
def set_full_name(self, full_name):
self.__full_name = full_name
def get_gender(self):
return self.__gender
def set_gender(self, gender):
self.__gender = gender
def get_current_age(self):
return self.__current_age
def set_current_age(self, current_age):
self.__current_age = current_age
def print_info(self):
print(f"氏名: {self.__full_name}")
print(f"年齢: {self.__current_age}")
print(f"学籍番号: {self.__student_id}")
print(f"性別: {self.__gender}")
def study(self):
print("私は勉強しています。将来、たくさんのお金を稼ぎたいです。")
# テスト
if __name__ == "__main__":
student = Student(1, "田中太郎", "男性", 26)
student.print_info()
3. データクラス(エンティティクラス)
3.1 紹介
3.1.1 データクラスとは何か?
データクラス(またはエンティティクラス)は、主にデータを保持するための特殊な形式のクラスです。ビジネスロジックは持たず、純粋にデータの格納と取得に特化しています。
3.1.2 データクラスの使い方?
データクラスはデータの保存のみを担当し、データの処理は他のクラスに任せて、データとビジネスロジックを分離することを実現します。
# データクラスの例
class Student:
def __init__(self, name, score):
self.__name = name
self.__score = score
# ゲッターとセッター
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
def get_score(self):
return self.__score
def set_score(self, score):
self.__score = score
# 成績が合格かどうかを表示するメソッド
# 成績レベルを表示するメソッド
# ...
# ビジネスロジックを担当するクラス
class StudentOperator:
def __init__(self, student):
self.student = student
def check_pass(self, passing_score):
if self.student.get_score() >= passing_score:
print(f"{self.student.get_name()}は合格です。")
else:
print(f"{self.student.get_name()}は不合格です。")
3.1.3 まとめ:
- データクラスの役割と注意点は?
データクラスはデータを保存するためのPythonクラスであり、オブジェクトを作成して特定の事物のデータを保存するために使用できます。データクラスはデータの保存のみを担当し、データの処理は他のクラスに任せて、データとビジネスロジックを分離します。 - データクラスの特徴は?
メンバー変数はプライベートにし、ゲッターとセッターを提供する必要があります。無引数コンストラクタは必須であり、引数付きコンストラクタを持つこともできます。
3.2 演習ケース
ケース: オブジェクト指向総合ケース - 映画情報システムを模倣する
要件
- システム内のすべての映画を表示する(各映画は:名前、価格を表示する)。
- ユーザーが映画のID(id)に基づいて特定の映画の詳細情報を検索できるようにする。
目標
- 学んだオブジェクト指向プログラミングを使用して、上記2つの要件を実現する。
完全なコード
# 映画のエンティティクラス
class Movie:
def __init__(self, movie_id, title, director, price):
self.__movie_id = movie_id
self.__title = title
self.__director = director
self.__price = price
# ゲッター
def get_movie_id(self):
return self.__movie_id
def get_title(self):
return self.__title
def get_director(self):
return self.__director
def get_price(self):
return self.__price
# 詳細情報を表示するメソッド
def print_details(self):
print(f"ID: {self.__movie_id}")
print(f"タイトル: {self.__title}")
print(f"監督: {self.__director}")
print(f"価格: {self.__price}円")
# 映画の操作クラス
class MovieOperator:
def __init__(self):
# 映画のリストを初期化
self.movies = [
Movie(1, "出拳吧,妈妈", "康晓白", 100),
Movie(2, "一点就到家", "许宏宇", 79),
Movie(3, "月球陨落", "罗兰", 100),
Movie(4, "奇幻冒险", "思明", 60),
Movie(5, "长津湖", "徐克", 90)
]
def display_all_movies(self):
"""すべての映画を表示する"""
for movie in self.movies:
print(f"{movie.get_title()} - 価格: {movie.get_price()}円")
def find_movie_by_id(self, movie_id):
"""IDで映画を検索する"""
for movie in self.movies:
if movie.get_movie_id() == movie_id:
movie.print_details()
return
print(f"ID {movie_id} の映画が見つかりませんでした。")
# テスト
if __name__ == "__main__":
operator = MovieOperator()
print("--- すべての映画を表示 ---")
operator.display_all_movies()
print("
--- 映画ID 3の詳細を検索 ---")
operator.find_movie_by_id(3)
print("
--- 映画ID 10の詳細を検索 ---")
operator.find_movie_by_id(10)