このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。

【Python入門】属性を外部から隠蔽する

属性を外部から隠蔽する

 オブジェクト指向プログラミングでは、各オブジェクトが持つ内部データや処理の一部を外部から直接操作されないように隠蔽(カプセル化)することが望まれます。Pythonでは、厳密なアクセス制御は存在しませんが、命名規約とマングリング機能を活用することで、意図しない操作を防ぐ仕組みを実現できます。ここでは、属性を外部から隠蔽する基本的な考え方、慣習、およびマングリングの仕組みについて、表や具体的なプログラム例を用いて解説します。

プログラムのダウンロード

 「ダウンロード」から、JupyterLab で実行できるサンプルプログラムがダウンロードできます。ファイルは、ESET Endpoint Securityでウイルスチェックをしておりますが、ダウンロードとプログラムの実行は自己責任でお願いいたします。

1.属性隠蔽の基本概念

1.1. 隠蔽の目的と背景

 他の言語(C++やJava)では、public、protected、private といったアクセス修飾子を用いて、属性へのアクセスを厳格に制御します。
 Pythonではすべての属性が公開されているため、プログラマ同士の合意や慣習で「この属性は外部からいじらないでほしい」という意図を示します。

1.2. 命名規則による隠蔽の手法

Pythonでは以下のような命名規則を利用して、属性の隠蔽意図を表現します。

命名パターン説明
公開属性price外部から自由にアクセス可能(隠蔽されていない属性)
慣習的な保護属性_price「この属性は内部用です」という意図を示すが、アクセス自体は可能
マングリング属性__priceクラス外部から直接アクセスしにくくなる(内部で _クラス名__price に変換される)

2.属性隠蔽の実践例

ここでは、書籍情報を管理するためのクラス Book を例に、属性隠蔽の手法を解説します。

2.1. 公開属性と慣習的保護属性の比較

 まずは、隠蔽を行わずに公開属性として定義した場合と、慣習的に先頭にアンダースコアを付けた場合の例を示します。

【公開属性の場合】

class Book:
    def __init__(self, title, price):
        self.title = title    # 公開属性
        self.price = price    # 公開属性

    def show(self):
        print(self.title, self.price)

# オブジェクト生成と属性操作
book1 = Book("Python入門", 3000)
book1.price //= 2  # 外部から自由に操作可能
book1.show()       # 出力: Python入門 1500

実行結果

Python入門 1500

この例では、price 属性が公開されているため、外部から直接変更可能です。

【慣習的保護属性の場合】

class Book:
    def __init__(self, title, price):
        self._title = title   # アンダースコア付き:外部操作は推奨されないが可能
        self._price = price

    def show(self):
        print(self._title, self._price)

# オブジェクト生成と属性操作
book2 = Book("Python応用", 4000)
book2._price //= 2  # 慣習上は避けるべきだが、実際には操作できる
book2.show()       # 出力: Python応用 2000

実行結果

Python応用 2000

 この場合、_price という命名により「内部用」の属性であると暗黙の了解を示していますが、アクセス自体は可能です。

2.2. ダブルアンダースコアによるマングリング

 より厳密に外部からのアクセスを防ぎたい場合、属性名の先頭に2個のアンダースコアを付けます。これにより、属性名は自動的に _クラス名__属性名 という形式に変換され、直接アクセスしにくくなります。

【マングリング属性の場合】

class Book:
    def __init__(self, title, price):
        self.__title = title   # マングリングが適用される
        self.__price = price

    def show(self):
        print(self.__title, self.__price)

# オブジェクト生成
book3 = Book("Python実践", 5000)

# 以下の操作はAttributeErrorとなる(直接アクセスできない)
# book3.__price //= 2

# 正しい操作方法として、内部でアクセスするか、名前マングリングされた名前を使う
# 例: book3._Book__price //= 2
book3._Book__price //= 2
book3.show()  # 出力: Python実践 2500

実行結果

Python実践 2500

 この例では、__price は外部からは book3.__price という名前でアクセスできず、名前マングリングによって内部的には _Book__price となっています。これにより、意図しない操作を防ぎ、クラス内部での整合性を保つことができます。

まとめ

ここでは、Pythonにおける属性の外部隠蔽の考え方と実践方法について解説しました。

  • すべての属性は基本的に公開されるが、アンダースコアの付加によりアクセス意図を示すことができる。
  • 先頭に1個のアンダースコア(_)を付けると「内部用」として扱われるが、完全な保護にはならない。
  • 先頭に2個のアンダースコア(__)を付けると、名前マングリングが適用され、外部からの直接アクセスが難しくなる。

 これらの仕組みを理解することで、オブジェクトの内部状態を保護し、より堅牢なプログラム設計を実現することが可能となります。