Python はそのシンプルさと読みやすさで知られていますが、オブジェクト指向プログラミング (OOP) に関しては、堅牢なコードを作成するために重要であるあまり議論されていないメカニズムがいくつかあります。そのようなメカニズムの 1 つが名前のマングリングです。この記事では、名前マングリングとは何か、Python が名前マングリングを使用する理由、およびそれが複雑なクラス階層での名前の衝突を防ぐ方法について説明します。
Python では、クラス内のメソッドをサブクラスによってオーバーライドできます。ただし、サブクラスが親クラスの属性またはメソッドを意図せずオーバーライドする場合、名前の競合が発生する場合があります。名前マングリングは、Python がこれらの競合を回避するために、特にプライベートであることを意図された属性に対して使用するメカニズムです。
Python の名前マングリングは、プライベート クラス プロパティが誤ってアクセスされオーバーライドされるリスクを最小限に抑えるために、インタプリタがプライベート クラス プロパティの名前を変更する機能です。これにより、クラス属性に一定レベルのプライバシーが提供されますが、厳密には強制されません。ただし、これは厳格な強制ではありません。
Python では、先頭に 2 つのアンダースコア (__) があり、末尾のアンダースコアが 1 つしかない識別子は、名前のマングリングの対象になります。インタプリタは、名前にクラス名を接頭辞として付けて名前を変換します。
特にサブクラスが親クラスの変数をオーバーライドする可能性のある独自の変数を持つ可能性がある状況で、名前の競合を防ぐために、Python は名前マングリングを実装します。名前のマングリングはこの問題に対処します。
from datetime import datetime, timedelta from time import time, sleep class Machine: def __init__(self, id): self.id = id self._started = time() def uptime(self): return time() - self._started class PetrolMachine(Machine): def __init__(self, id): super().__init__(id) self._started = datetime.now() def cost(self): duration = datetime.now() - self._started return duration/timedelta(seconds=60) *0.02 worked = PetrolMachine('12345') sleep(0.123) print(f"uptime : {worked.uptime():.2f}")
この例では、Machine クラスが ID を保存し、Python の time() 関数を使用して開始時刻を記録します。稼働時間を要求すると、現在時刻と開始時刻の差が計算され、浮動小数点数として保存されます。ただし、サブクラス PetrolMachine は、datetime.now() を使用して開始時刻を保存します。稼働時間を計算しようとすると、プログラムは start_time が浮動小数点数であることを期待しているため、エラーをスローしますが、現在は datetime オブジェクトになっています。この名前の競合は、サブクラスの属性が親クラスの属性を意図せずオーバーライドした場合に発生する可能性があります。名前のマングリングは、この問題を回避するのに役立ちます。
では、名前のマングリングはこの問題の解決にどのように役立つのでしょうか?クラス属性の先頭に 2 つのアンダースコアが付けられている場合、Python はクラス名を接頭辞として含めるように名前を内部的に変更します。ここでは、名前のマングリングを使用して名前の競合を回避するために Machine クラスを変更する方法を示します。
次に示すように、Machine クラスの __started 属性に名前マングリングを適用することで、エラーを解決できます。
from datetime import datetime, timedelta from time import time, sleep class Machine: def __init__(self, id): self.id = id self.__started = time() def uptime(self): return time() - self.__started class PetrolMachine(Machine): def __init__(self, id): super().__init__(id) self._started = datetime.now() def cost(self): duration = datetime.now() - self._started return duration/timedelta(seconds=60) *0.02 worked = PetrolMachine('12345') sleep(0.123) print(f"uptime : {worked.uptime():.2f}")
名前マングリングを表現する簡単な方法を以下に示します。クラス ClassA があり、名前がマングルされた private_variable が 1 つあります。
class MyClass: def __init__(self): self.__private_var = "I am private" def get_private_var(self): return self.__private_var my_object = MyClass() print(my_object.get_private_var()) # This works print(my_object.__private_var)
変数 __private_var の名前がマングルされているため、2 番目の print() は AttributeError を発生させます。 Python は内部的に名前を _MyClass__private_var に変更し、クラスの外部からアクセスするのを難しくしています。
Python の名前マングリングは偶発的なアクセスを防ぐように設計されていますが、厳密なプライバシーは強制されません。完全なマングル名を使用してマングルされた属性にアクセスすることはできますが、これはお勧めできません。仕組みは次のとおりです。 my_object._MyClass__private_var
print(my_object._MyClass__private_var)
簡単な例で説明します
class MyClass: def __init__(self): self._protected_var = "I'm protected" self.__private_var__ = "I'm not mangled"
Python では、先頭の 1 つのアンダースコア (例: _protected_var) は、属性が「保護」されており、クラスの外部から直接アクセスすべきではないことを示します。ただし、Python ではこれが強制されません。対照的に、先頭に 2 つのアンダースコアがある名前 (__private_var など) は、誤ってオーバーライドされるのを防ぐために破壊されます。重要なのは、両側に二重アンダースコアがある名前 (__special__ など) は破壊されず、マジック メソッドなどの特殊な使用例のために予約されているということです。
_ これらの制限にもかかわらず、名前マングリングは Python の OOP ツールキットの有用なツールであり続けます。これはプライバシーを厳密に強制するものではありませんが、名前の競合や偶発的な属性の上書きを防ぐのに役立ちます。名前マングリングを理解すると、特に複雑なクラス階層を扱う場合に、より堅牢で保守しやすいコードを作成できるようになります。あなたのプロジェクトで試してみて、以下のコメントであなたの経験や質問を共有してください!_
以上がPython での名前マングリングの探求: それが何であり、どのように機能するかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。