본문 바로가기
Programming/Python

[Python] "_" in Python (Underbar)

by dev_ss 2022. 10. 30.

Python의 Code는 PEP-8에 의거하여 작성하는 것을 권장하고 Snake_case를 기반으로 표기를 하는데 Python 내에서도 Underbar(_)는 다양한 용도로 사용한다.

 

다음은 용도에 대하여 분류하였다.

 

1. Interpreter의 마지막 값의 대용으로 사용

Python에서 연산을 마쳤을 때 Underbar는 Interpreter에서 마지막으로 출력한 값으로 사용할 수 있다.

python3
>>> 1+1
2
>>> _+1
3
>>> _*3
9

2. 버리는 값

Python에서 Code를 입력할 때 버리는 값으로 Underbar를 많이 사용한다. 하지만 기능적으로 의미가 있는 것은 아니고 버리는 값이라고 명시적으로 표기해 주는 것이다.

 

Example : 

one = "*"
two = "**"
for _ in range(3):
    print(one)
    print(two)

 

Result :

*
**
*
**
*
**

3. 숫자 자릿수 표현

Python에서는 기본적으로 정수를 바인딩할 때 다음과 같이 표현한다.

number = 123456789

하지만 여기서 Underbar를 이용해서 숫자 자릿수를 나누어줄 수 있다.

number = 123_456_789

결과 값은 동일하다.

 

Example :

number = 123456789
number_ = 123_456_789
print(number)
print(number_)

Result :

123456789
123456789

4. 객체의 사용처를 구분할 때

해당 Case에는 4가지로 볼 수 있는데 앞에 1개, 뒤에 1개, 앞에 2개 앞 뒤로 2개씩 붙은 총 4개의 Case가 있다.

1) _case (Underbar가 앞에 1개)

첫 번째 케이스로 _가 이름 앞에 붙어있을 때는 해당 모듈 내에서만 객체를 사용하겠다는 의미를 지니고 있다.

 

# Python file with some functions
# plus_minus.py

def _plus(a,b):
    return a+b

def minus(a,b):
    return a-b

plus_minus.py라는 파일을 만들고 main.py 에서 import를 하여 사용하는 상황을 가정을 해보았을 때

 

Example : 

# main.py
from plus_minus import _plus

print(_plus(1,2))

Result : 

3

위의 경우는 특정 함수를 가져온다고 명시하여 주었기 때문에 에러가 없다.


하지만 특정 함수를 지정하여 import를 해주는 것이 아니라면 에러를 발생한다.

 

Example : 

# main.py
from plus_minus import *

print(_plus(1,2))

Result :

NameError: name '_plus' is not defined

minus의 함수는 잘 작동한다.

 

Example : 

# main.py
from plus_minus import *

print(minus(1,2))

Result :

-1

2) case_(Underbar가 뒤에 1개)

이번 케이스는 기능적으로 특별한 의미가 있지는 않으나 변수를 지정하거나 함수를 선언할 때 Python 내 자체 함수들과 충돌을 방지하고자 할 때 사용한다.

 

Example :

int = 3
print(int)

Result : 

3

결과는 3으로 Terminal에서 반환하지만 int의 함수를 덧씌움으로 해당 Python 내장 함수를 사용하지 못한다.

 

그렇기에 int_와 같은 표기 방식으로 충돌을 방지하고자 이름 뒤에 Underbar를 이용한다.


3) __case(Underbar가 앞에 2개)

이름 앞에 Underbar가 2개 붙여서 사용하는 것을 'name mangling'이라고 하며 특수한 기능을 가지고 있다.

 

Python은 class를 선언할 때 다른 언어처럼 Public/Protected/Private 같은 class의 Field를 지정하지 않고 전부 Public으로 사용한다.

그렇기에 name mangling으로 Private와 유사하게 접근을 제한하거나 상위 class의 Overriding을 방지할 수 있다.

 

아래는 일반적으로 class 선언 후 속성을 출력한 것이다.

 

Example :

class Apple:
    def __init__(self):
        self.pc = "imac"
        self.mobile = "iphone"
        self.watch = "apple-watch"

test = Apple()
print(test.pc)
print(test.mobile)
print(test.watch)

Result :

imac
iphone
apple-watch

하지만 name mangling을 이용하면 다른 결과를 보인다.

 

Example :

class Apple:
    def __init__(self):
        self.pc = "imac"
        self.mobile = "iphone"
        self.__watch = "apple-watch"

test = Apple()

print(test.pc)
print(test.mobile)
print(test.__watch)

Result :

imac
iphone
Traceback (most recent call last):
  File "/Users/user/Desktop/main.py", line 10, in <module>
    print(test.__watch)
AttributeError: 'Apple' object has no attribute '__watch'

다음과 같이 name mangling을 이용한 곳만 AttributeError를 나타내는 것을 볼 수 있다.


그러면 '어떻게 해당 속성에 접근이 가능한가?'라는 의문이 있을 수 있는데 다음과 같이 확인할 수 있다.

 

Example :

class Apple:
    def __init__(self):
        self.pc = "imac"
        self.mobile = "iphone"
        self.__watch = "apple-watch"

test = Apple()
print(dir(test))

Result :

['_Apple__watch', '__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__', 'mobile', 'pc']

첫 번째로 있는 속성은 '_Apple__watch'이고 이는 name mangling으로 만들어진 속성을 의미하며 해당 명칭으로 접근을 해야 속성을 불러올 수 있다.

 

Example :

class Apple:
    def __init__(self):
        self.pc = "imac"
        self.mobile = "iphone"
        self.__watch = "apple-watch"

test = Apple()
print(test._Apple__watch)

Result :

apple-watch

4) __case__ (Underbar가 앞 뒤로 2개씩)

해당 case는 Python을 접한 사람들은 어느 정도 보았을 것이다.

    ex ) __init__ (생성자)와 __del__ (소멸자)와 같은 case

 

Underbar를 앞 뒤로 2개씩 붙여서 사용하는 경우는 Python에 자체적으로 정의한 변수 또는 함수라는 표기이다.


Python내 자체 변수/ 함수들을 보면(ex. 이전 case에서 class 내부를 보았을 때) 앞 뒤로 표기되어 있는 Underbar가 2개씩 있는 것을 확인할 수 있다.

 

Example :

class Apple:
    def __init__(self):
        self.pc = "imac"
        self.mobile = "iphone"
        self.__watch = "apple-watch"

test = Apple()
print(dir(test))

Result :

['_Apple__watch', '__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__', 'mobile', 'pc']

 

(Python 자체함수 dir(object)를 이용하여 확인 가능)

 


 

별도의 특수한 기능있는 것이 아니고 Python내에서 합의하여 사용하고 있는 사항이며 Python내 함수/변수를 Overriding하여 사용하는 경우나 기존의 함수/변수를 호출하여 사용하는 경우를 제외하고는 일반적으로 사용할 일이 없다.

반응형

'Programming > Python' 카테고리의 다른 글

[Python] 생성자(__init__)와 소멸자(__del__)  (0) 2022.10.21
[Python] GIL(Global Interpreter Lock)  (3) 2022.10.15
[Python] 파이썬이란  (0) 2022.10.13