파이썬(python) - _(언더바, 언더스코어)
파이썬에서 _(이하 '언더바'로 통일)의 역할
다른 언어와 다르게 파이썬에서 언더바는 Snake case로 네이밍을 할 때 외에도 다양한 사용처가 있습니다.
이는 아래와 같이 크게 4가지로 분류할 수 있습니다.
1. 인터프리터에서의 마지막 값
2. 무시하는 값
3. 숫자 리터럴의 자릿수를 구분하는 역할
4. 네이밍
1. 인터프리터에서의 마지막 값
우선 첫 번째로 인터프리터 내에서의 마지막 값을 의미합니다.
>>> 1+1
2
>>> _ + 2
4
2번째 명령줄의 언더바가 바로 윗줄의 2를 의미합니다.
너무 간단하니 바로 넘어가겠습니다.
2. 무시하는 값
여기서 무시하는 값이란, 튜플 등을 언패킹 할 때 특정 값을 버리는 용도로 언더바를 사용할 수 있다는 의미입니다.
n_tuple = (1, 2, 3, 4, 5)
a, b, c, _, e = n_tuple # 4는 무시하고 1,2,3,5만 언패킹
print(a, b, c, e)
출력 결과
1 2 3 5
*(Asterisk)를 이용하여 여러개의 값을 무시할 수도 있습니다.
n_tuple = (1, 2, 3, 4, 5)
a, *_, b = n_tuple # 2,3,4를 무시하고 1과 5만 언패킹
print(a, b)
출력 결과
1 5
for문에도 응용할 수 있습니다.
all_tuple = []
tuple_1 = (1, 2)
tuple_2 = (3, 4)
tuple_3 = (5, 6)
all_tuple.append(tuple_1)
all_tuple.append(tuple_2)
all_tuple.append(tuple_3)
for _, even in all_tuple:
print(even)
출력 결과
2
4
6
튜플 3개로 이루어진 리스트를 for문으로 언패킹하면서 print하는데
이 때 튜플의 첫 번째 값을 _로 무시하고 두 번째 값만 출력하는 코드입니다.
따라서 실행하면 짝수만 출력이 됩니다.
3. 숫자 리터럴의 자릿수를 구분
말 그대로 숫자의 자릿수 구분을 위해 사용할 수 있습니다.
그냥 숫자라고 써도 되는데 있어보이려고 리터럴이라고 굳이 덧붙였습니다.
number = 100_000_000
number_2 = 0x_5f_5e_100
print(number, number_2)
출력 결과
100000000 100000000
4. 네이밍
네이밍을 위해 사용되는 언더바는 4가지 경우로 구분할 수 있습니다.
4-1. 언더바가 앞에 하나 붙은 경우 (ex: _variable)
- 이 경우는 모듈 내에서만 해당 변수/함수를 사용하겠다는 의미입니다.(private) 특히 협업을 할 때 다른 팀원에게 '이 변수/함수는 이 모듈 내부에서만 사용할 거다'라고 명시적으로 힌트를 줄 수 있습니다.
단, 완전한 의미의 private는 아니기 때문에 여전히 해당 변수/함수는 접근하거나 사용할 수 있습니다. 파이썬은 private와 public의 확실한 구분이 없습니다.
하지만 외부 모듈에서 해당 모듈을 from module_name import * 식으로 전체 임포트 할 때 앞에 언더바가 붙은 변수/함수는 임포트를 하지 않습니다.
test_module.py
def _hi():
print('hi')
def hello():
print('hello')
test_module이라는 모듈을 만들었습니다. 그리고 함수를 2개 선언했는데 하나는 앞에 언더바를 붙이고 하나는 붙이지 않았습니다.
from test_module import *
hello()
_hi() # 에러 발생
그리고 다른 모듈에서 해당 모듈을 from과 *로 임포트했습니다.
이 상태로 아까의 내부 함수를 2개 실행하려고 하면, 앞에 언더바가 붙은 함수를 찾을 수 없다면서 에러를 발생시킵니다.
하지만 상술했듯이 이는 완전한 private는 아니기 때문에 직접 가져오거나 호출하면 사용이 가능합니다.
from test_module import hello, _hi
hello()
_hi() # 에러 없이 정상 실행
4-2. 뒤에 언더바가 하나 붙은 경우 (ex: variable_)
- 이 경우는 파이썬 키워드와 변수/함수명의 충돌을 피하기 위해 사용하는 경우입니다.
예를 들어, list나 print같은 키워드를 변수/함수명으로 사용하고 싶을 때 list_, print_ 와 같이 사용합니다.
list_ = [1, 2, 3, 4, 5]
def print_(args):
print('hi')
print(args)
print_(list_)
출력 결과
hi
[1, 2, 3, 4, 5]
4-3. 앞에 언더바가 두 개 붙은 경우 (ex: __variable)
- 이 경우는 네이밍 룰이라기 보단 문법적인 요소로 생각하셔야 합니다. 바로 네임 맹글링(name mangling)을 위한 경우입니다. 맹글링은 '짓이기다'라는 뜻으로 쉽게 말하자면 파이썬이 해당 변수/함수의 이름을 짓이겨서 바꿔버리는 것을 의미합니다.
맹글링을 당한 변수/함수는 본연의 이름으로 접근할 수 없게 됩니다.
감이 잘 안오니 좀 더 자세히 알아봅시다.
class TestClass():
def __init__(self):
self.name = "곽두팔"
self.gender = "male"
self.__age = 32
man = TestClass()
print(man.name)
print(man.gender)
print(man.__age) # AttributeError 발생
클래스 내에 멤버변수 name, gender를 만들고 맹글링을 적용한 변수 __age를 선언했습니다.
이후 man 변수에 인스턴스를 저장하고 멤버를 출력합니다.
실행하면 name, gender는 잘 출력되지만 __age에서 AttributeError가 발생합니다.
그렇다면 저 __age는 도대체 어떻게 접근 할 수 있을까요?
print(dir(man))
['_TestClass__age', '__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__', 'gender', 'name']
dir 함수로 까보면 마지막에 gender, name이 보이고 바로 첫 인덱스에 _TestClass__age라는 이름이 보입니다.
print(man._TestClass__age) # 32 출력
이 녀석을 출력하면 비로소 우리가 찾던 값인 32가 나옵니다.
즉 언더바 2개로 맹글링을 적용한 변수/함수는 _클래스명__변수/함수명 형태로 이름이 변경된다는 사실을 알 수 있습니다.
맹글링이 그렇다면 왜 필요한지 궁금한 분들이 계실 것 같은데 이는 다음 포스팅에서 간단히 다뤄보겠습니다.
4-4. 앞 뒤로 언더바가 2개씩 붙은 경우 (ex: __variable__)
- 이는 파이썬이 특별히 정의한 변수나 함수에 사용되는 네이밍 룰입니다. 사실 이 네이밍 룰을 사용해서 직접 함수를 만드는 없다고 보셔도 무방하고 대부분 오버라이딩 할 때 사용하게 됩니다.
이 네이밍 룰이 적용된 함수는 매직 메소드(Magic Method)나 던더 메소드(Dunder Method, Double Underscore Method)라고 부릅니다.
가장 대표적으로 __init__함수가 있습니다. __init__함수는 클래스의 인스턴스가 생성될때 가장 처음으로 실행되는 함수로 생성자 역할을 합니다.
class TestClass():
def __init__(self):
print("Hello World")
test = TestClass()
실행하면 인스턴스가 생성되면서 "Hello World"를 출력하게 됩니다.