실무 관점에서 보면, Python에는 다른 언어처럼 엄격한 의미의 비공개(private) 멤버 변수가 존재하지 않습니다. 다만 변수나 메서드 이름 앞에 밑줄 두 개(__)를 붙이면 비공개로 취급하는 것이 일종의 관례(convention)이며, 대부분의 Python 코드가 이 방식을 따르고 있습니다.
다음 예제를 통해 이 개념을 자세히 살펴보겠습니다.
예제 코드: privateVar1.py
class myClass:
__privateVar = 27
def __privMeth(self):
print("I'm inside class myClass")
def hello(self):
print("Private Variable value: ", myClass.__privateVar)
foo = myClass()
foo.hello()
foo.__privateMeth위 프로그램에서 __privMeth는 비공개 메서드이고, __privateVar는 비공개 변수입니다. 실행 결과를 통해 어떻게 동작하는지 확인해 보겠습니다.
실행 결과
Private Variable value: 27 Traceback (most recent call last): File "C:/Python/Python361/privateVar1.py", line 12, in <module> foo.__privateMeth AttributeError: 'myClass' object has no attribute '__privateMeth'
출력 결과를 보면, 클래스 외부에서는 비공개 메서드와 비공개 변수 모두 접근할 수 없다는 것을 알 수 있습니다. 마지막 줄에서 AttributeError가 발생한 이유가 바로 외부에서 비공개 메서드에 접근하려 했기 때문입니다.
반면 클래스 내부에서는 비공개 변수에 자유롭게 접근할 수 있습니다. hello() 메서드 안에서 __privateVar에 정상적으로 접근하여 "Private Variable value: 27"이 출력된 것을 확인할 수 있습니다.
네임 맹글링(Name Mangling)의 원리
즉, 클래스 내부의 모든 변수와 메서드는 기본적으로 공개(public)입니다. 데이터 멤버를 비공개로 선언한다는 것은 해당 멤버가 클래스 내부에서만 접근 가능하고 외부에서는 접근할 수 없음을 의미합니다. 이렇게 변수나 메서드를 비공개로 만드는 기법을 네임 맹글링(name mangling)이라고 부릅니다.
__spam 형태의 식별자(앞에 밑줄 두 개 이상, 뒤에 밑줄은 최대 한 개)는 텍스트적으로 _classname__spam으로 치환됩니다. 여기서 classname은 현재 클래스 이름에서 선행 밑줄을 제거한 값입니다. 이러한 맹글링은 식별자가 클래스 정의 내부에 존재하기만 하면 문법적 위치와 무관하게 적용됩니다.
밑줄 두 개로 시작하는 이름은 본래 상속 구조에서 서브클래스가 부모 클래스의 속성이나 메서드를 실수로 덮어쓰는(overriding) 상황을 방지하기 위한 목적으로 설계되었습니다.
참고: 밑줄 한 개(_)와 밑줄 두 개(__)의 차이
밑줄 한 개(_)로 시작하는 이름은 "내부 사용용"이라는 암시적인 표시일 뿐 강제력이 없으며, from module import * 구문에서만 제외됩니다. 반면 밑줄 두 개(__)를 사용하면 네임 맹글링이 적용되어 외부에서의 접근이 실질적으로 차단되므로, 진정한 의미의 비공개 속성이 필요하다면 밑줄 두 개를 사용하는 것이 좋습니다.