All About Underscores in Python
- Python을 공부하다 보면 클래스 안에서 underscore와 double underscores로 시작하거나 끝나는 변수 명을 많이 보게 됩니다. 변수/함수명이 test라면,
_test
,test_
,__test
,__test__
와 같은 변수명들을 흔히 발견할 수 있습니다. 이 blog에서는 이 4가지 single underscore과 double underscores의 의미에 대해 알아보고, 어떻게 Python 프로그램에 영향이 가는지 알아보겠습니다.
1. 한개의 underscore로 시작하는 함수/변수명 : _test
한개의 underscore가 prefix (접두사)로 들어가는 변수/함수명은 컨벤션임을 의미 합니다. 즉, Python 사용자들끼리는 알고있는 의미이지만, 프로그래밍 작동에 있어서는 영향을 끼치지 않는다는 뜻입니다. Underscore prefix는 다른 Python 사용자들에게 underscore로 시작하는 함수/변수명은 내부용 (internal use)이라는 것을 알려줍니다. 이 컨벤션은 PEP 8, Python 코드 스타일 가이드, 에도 정의 되어 있습니다.
하지만, 이 컨벤션은 Python 인터프리터가 작동하는데 있어서 영향을 주지 않습니다. Python은 변수/함수 명이 “private” 이냐 “public”이냐에 있어서 큰 구별을 두지 않습니다. 그렇기에 한개의 underscore를 함수/변수명 앞에 붙이는 것은 “이 함수/변수명은 이 클래스의 퍼블릭 인터페이스에서 사용하지 않는 것을 권장하니 그냥 두세요” 라고 말하는 것과 같습니다.
아래에 Test라는 class를 만들어 single underscore가 들어간 함수명에 접근해 보도록 하겠습니다.
1 | class Test: |
1 | t = Test() |
python
programming
test2 변수 앞에 underscore를 붙이는 것이 클래스에 있는 test2변수에 접근하는 것을 막지 않았음을 볼 수 있습니다. 위에 언급한 것 처럼 그저 컨벤션 이기 때문입니다. 하지만 이 underscore prefix는 module을 import할 때 영향을 줍니다. 아래와 같이 test_module.py
를 만들어서 import all (import *)를 해 보겠습니다.
1 | with open('test_module.py', 'w') as f: |
1 | from test_module import * |
python
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-911f6273b411> in <module>
1 from test_module import *
2 print(public_func())
----> 3 print(_internal_func())
NameError: name '_internal_func' is not defined
위에서 _internal_func()
를 프린트 하려고 할 때 name error가 나는 것을 확인할 수 있습니다. Python은 import 를 사용할 때 한개의 underscore로 시작하는 변수/함수들을 import하지 않습니다. 하지만 PEP 8에서도 import 은 될 수 있으면 피해야 한다고 나와있습니다. 그 이유는, 어떤 이름들이 네임스페이스 안에 있는지 명확하지 않기 때문입니다. 일반 import는 single underscore 컨벤션에 영향을 받지 않습니다. 아래에 일반 import를 사용하여 확인해 봅니다.
1 | import test_module |
python
programming
2. 한개의 underscore로 끝나는 함수/변수명 : test_
프로그래밍을 하다 보면 그 변수에 가장 맞는 이름이 Python 언어에게 이미 빼앗긴(?) 경우가 있을 수 있습니다. 에를들어, class를 나타내고 싶은 함수인데 class라는 이름은 이미 python에서 class를 만드는데 사용되는지라 사용될 수 없습니다. 그럴 때 class_
처럼 마지막에 underscore를 붙여줌으로서 충돌을 방지해 줍니다. 이 내용 또한 PEP 8에 적혀있는 컨벤션 입니다.
3. 두개의 underscores로 시작하는 함수/변수명 : __test
두개의 underscores로 시작하는 함수/변수명은 Python 인터프리터가 서브클래스들 (subclasses)간의 이름 충돌이 일어나지 않게 하기 위해 클래스의 속성(함수/변수) 이름을 다시 쓰게 합니다. 이것은 name mangling 이라고 부릅니다. 인터프리터가 변수/함수의 이름을 바꿈으로서 클래스가 더 확장이 되었을 때 충돌이 일어나는 것을 방지합니다. 예제로 알아 보겠습니다.
1 | class Test: |
아래는 객체 t
가 가지고 있는 속성들 입니다. test1
과 _test2
는 보이는데 __test3
이 보이지 않습니다. 그 대신, _Test__test3
이라는 속성이 존재 합니다. 이 것이 name mangling입니다. Python 인터프리터가 변수/함수 이름을 바꿈으로서 그 변수/함수가 서브 클래스에서 overidden되는 것을 방지 합니다.
1 | t = Test() |
['_Test__test3',
'__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'_test2',
'test1']
Test를 상속 받은 클래스를 만들어서 Test클래스의 constructor에 있는 속성들을 override 하려는 시도를 해 봅니다.
1 | class TestClassExtended(Test): |
아래와 같이 TestClassExtended로 만든 객체 t2를 가지고 속성에 print 함수를 통해 접근해 봅니다. 하지만 t2.__test3
을 프린트 하려고 할 때 attribute error가 나는 것을 확인할 수 있습니다. 위에서 언급한 name mangling 때문 입니다.
1 | t2 = TestClassExtended() |
overriding
overriding
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-21-b9b94d755c5a> in <module>
2 print(t2.test1)
3 print(t2._test2)
----> 4 print(t2.__test3)
AttributeError: 'TestClassExtended' object has no attribute '__test3'
t2의 속성들도 아래와 같이 확인해 봅니다. t2의 속성에서도 __test3
이 없으며, 대신 _TestClassExtended__test3
가 존재한다는 것을 확인할 수 있습니다. __test3
이 _TestClassExtended__test3
으로 바뀌면서 실수로 이름이 바뀌어 버리는 것을 막아 줍니다.
1 | dir(t2) |
['_TestClassExtended__test3',
'_Test__test3',
'__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'_test2',
'test1']
TestClassExtended로 만든 t2 객체의 _TestClassExtended__test3
와 Test로 만든 t객체의 _Test__test3
가 둘 다 존재한다는 것을 확인 합니다.
1 | print(t2._TestClassExtended__test3) |
overriding
is fun
위와 같이 직접 변수/함수명으로 불러오는 것은 되지 않지만, 다른 함수를 사용하여 name mangling된 변수/함수를 가져오는 것은 가능 합니다. 아래의 TestNameMangling 클래스를 만들어 확인해 봅니다.
1 | class TestNameMangling: |
아래와 같이, 다른 함수 get_mangling
을 사용하여 생성자 안에 있는 __test_mangling
의 값을 가져올 수 있지만, 직접 __test_mangling
변수는 접근이 불가하다는 것을 확인 할 수 있습니다.
1 | print(TestNameMangling().get_mangling()) |
python
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-28-afce42ce199f> in <module>
1 print(TestNameMangling().get_mangling())
----> 2 print(TestNameMangling().__test_mangling)
AttributeError: 'TestNameMangling' object has no attribute '__test_mangling'
함수 또한 같은 방법으로 접근이 가능합니다.
1 | print(TestNameMangling().call_manglingmethod()) |
programming
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-30-e221b1fb59b4> in <module>
1 print(TestNameMangling().call_manglingmethod())
----> 2 print(TestNameMangling().__manglingmethod())
AttributeError: 'TestNameMangling' object has no attribute '__manglingmethod'
4. 두개의 underscores로 시작하고 끝나는 함수/변수명 : __test__
name mangling은 이름이 두개의 underscores로 시작하고 끝나면 적용되지 않습니다.
1 | class TestNameMangling: |
'python'
하지만 두개의 underscores로 시작하고 끝나는 함수/변수명들은 특별한 경우에 사용 되기 위해 “예약”되어 있습니다. 자주 보이는 생성자 함수인 __init__
이나 객체를 callable하게 만들 때 사용하는 __call__
함수가 그 예 입니다. 이 dunder (Double UNDERscore) method (함수)는 magic method라고도 불립니다. 하지만 위의 예시처럼 변수의 이름을 지을 때, 두개의 underscore로 시작하고 끝나는 이름으로 짓는 것은 피해야 하는데 이유는 Python 언어가 바뀌거나 업그레이드 될 때 충돌을 방지해야 하기 때문입니다.
Summary
- 한개의 underscore로 시작하는 함수/변수명
_test
: 함수/변수가 내부용 (internal use)이라는 것을 Python 사용자들에게 알려주는 컨벤션이며 Python 인터프리터가 작동하는데에 영향을 주지 않습니다 (import * 를 제외하고) - 한개의 underscore로 끝나는 함수/변수명
test_
: Python 언어가 사용하는 키워드 (class, def, list등)들과의 충돌을 막기 위해 마지막에 underscore를 붙여 주는 컨벤션 입니다. - 두개의 underscores로 시작하는 함수/변수명
__test
: 클래스를 만들 때, name mangling 이 되며, Python interpreter가 코드를 읽음에 있어서 영향을 줍니다. - 두개의 underscores로 시작하고 끝나는 함수/변수명
__test__
: Python 언어에서 정의 되는 special methods (magic methods)입니다. 직접 클래스에서 속성을 만들 때에는 이 이름 짓는 방식을 피하는 것이 좋습니다 (혹시 있을 충돌 방지를 위해!)