파이썬

파이썬(python) - _(언더바, 언더스코어)

티베트 모래여우 2020. 10. 30. 10:19
반응형

파이썬에서 _(이하 '언더바'로 통일)의 역할

다른 언어와 다르게 파이썬에서 언더바는 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"를 출력하게 됩니다.

반응형