Python: Name Mangling

역시나, 아주 간단하게 정리해본다.

왜 Mangle 이란 다소 애매한 단어를 붙여서 한눈에 이해가 안되게 만들었을까? 물론, 애초에 Expansion 이라 붙였다해도 이해가 안되기는 마찬가지였겠지.

Mangle 에는 크게 두가지 뜻이 있다. 하나는 모양이 바뀔 때까지 강한 힘으로 부서뜨리거나 으스러 뜨려서 원래 모습을 알아보기 힘든 상태로 만들기로 풀어볼 수 있고, 다른 하나는 제대로된 정보를 전달하지 못함을 비판함이다. 물론, 여기에 명사로서 예전에 쓰던 탈수기라는 뜻도 있긴 하다.

명확하게 Name Mangling 이 어디에서 비롯됐는지는 모르겠다. C++ 등, 이 개념을 제대로 사용하고 있는 언어에 대해 잘 알고 있다면 쉽게 추측할 수 있겠지만, Python 에선 좀 애매하다.

Python 에선 Mangling(이게 정확히 무슨 뜻인지는 그냥 넘기고)보다는 Expansion(Learning Python 4th Ed. p747)으로 이해하는 편이 훨씬 쉽다. 이름(변수/메소드 등)이 난도질 되어 변형된다기 보다는, ‘확장’된다는 뜻이다.

뭔 말인지, 아주 간단하게 풀어본다. 역시 Learning Python 4th Ed. 을참고했다. (대략 p748 부근)


class C1:
    def __init__(self):
        self.X = 88
    def meth2(self):
        print(self.X)

class C2:
    def __init__(self):
        self.X = 99
    def methb(self):
        print(self.X)

class C3(C1, C2):
    def __init__(self):
        C1.__init__(self)
        C2.__init__(self)

i = C3()

i.X
99

i.meth2()
99

Class C1, C2 에 각각 self.X 라는 Attribute 가 있다. 워낙엔 각각 이름이 달라야 하는데, 프로그래밍을 하다 보니 이런 결과가 나왔다.
그런데다가, C3 는 이 두 클래스 모두를 상속받았다. C3 의 인스턴스인 i.X 는 과연, 어떤 값을??

결과는 C2 에서 정의된 값인 88 이 된다. i.meth2() 로 C1 메소드를 실행해봤지만, 결과는 99 그대로다. C2 를 따르는 이유는 간단한데, C3 에서 C2.__init__ 이 더 나중에 실행됐기 때문이다.
이걸 해결하려면..? 그냥 이름을 다르게 주면 된다. C1 에선 X1 으로, C2 에선 X2 등으로.

허나.. 프로그램 규모가 커지면 이렇게 충돌할 가능성이 아무래도 생길 수 밖에 없겠지? 이럴 때 C1네 식구, C2네 식구, 각각 따로 따로 챙길 수 있는 방법이 있다면 충돌의 소지를 없앨 수 있지 않을까? 그래서 나온게 바로 Pseudoprivate Attribute 다.


class C1:
    def __init__(self):
        self.__X = 88
    def meth2(self):
        print(self.__X)

class C2:
    def __init__(self):
        self.__X = 99
    def methb(self):
        print(self.__X)

class C3(C1, C2):
    C1.__init__(self)
    C2.__init__(self)

i = C3()

i.X
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-18-07e318dc68f7> in <module>
----> 1 i.X

AttributeError: 'C3' object has no attribute 'X'

i.__X
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-19-847b6a4eca96> in <module>
----> 1 i.__X

AttributeError: 'C3' object has no attribute '__X'

i.X 가 없다는 사실이야 당연하지만, i.__X 도 없다니?
없어진게 아니고, 변환(Mangled) 또는 확장(Expanded)됐고, 이게 바로 Python 에서 말하는 Name Mangling/Pseudoprivate Attribute 다.

그럼 뭘로 바뀌었나?
변수명에 자신의 출신을 나타내는 클래스가 포함되고, 클래스명 앞에는 홑밑줄이 붙는다.

i.__dict__
{'_C1__X': 88, '_C2__X': 99}

이렇게, _Class__Attribute 형식으로 변수가 새롭게 탄생됐다.
하지만, 이게 C++ 등에서 말하는 Private 하고는 거리가 멀다. 여전히 외부에서도 저 이름을 쓰면 접근이 가능하기 때문이다.

i._C1__X = 55

이론은 이렇고..
Mark Lutz, Learning Python 의 저자는 이 기법을 대규모이고, 여러 프로그래머가 협업하는 상황에서만, 그마저도 몇몇 제한된 이름에서만 사용하라고 한다.
같은 내용을, Effective Python(by Brett Slatkin) Item 42 에서 얘기하고 있다.


사실, 이 글엔 한가지 더 중요한 내용이 나와있다.
C3 에서, 상속받은 상위 클래스의 Constructor 를 저런 식으로 따로 따로 실행해줘야만 한다는 사실!
이 방식은 예전 스타일이고, 요즘은 super() 가 대세(?)라고는 하는데.. 여기까지 가면 고려할 사항도 많고, 머리 속에 하애질 수도 있다. MRO(Method Resolution Order) 에서 이 내용을 다루고 있는데, 아무튼 꽤 복잡 미묘한 문제가 된다.
하여, 이 내용은 다음 기회로! (다음 기회가 있을런지..?)

Author: 아무도안

안녕하세요. 글 남겨주셔서 고맙습니다.