https://www.datacamp.com/community/tutorials/role-underscore-python
이 글은 위 글의 번역 및 정리 글입니다.
파이썬관련 찾아볼 일이 있어서 보다가 언더바에 대한 내용들이 있어서 한번 정리합니다.
파이썬 개발자들 중 많은사람들이 파이썬에서 언더바의 역할에 대해 잘 모르는데, 이를 이용해서 코드 생산성을 늘릴 수 있습니다. 이번기회에 한번 알아봅시다.
for _ in range(100)
__init__(self)
_ = 2
만약 당신이 파이썬 개발자라면, 위와 같은 파이썬 코드들을 본 적이 있을 것입니다.
위에서 _는 각각 다른 조건에서 특별한 의미를 갖습니다.
일단 6개의 다른 용도가 있는데 차근차근 알아봅시다.
- 인터프리터에서의 사용
- 무시하는 값
- 루프에서 사용
- 숫자값의 분리
- 명명용
- 앞에 하나가 쓰이는 경우
- 뒤에 하나가 쓰이는 경우
- 앞에 두개가 쓰이는 경우
- 앞 뒤로 두개씩 쓰이는 경우
이제 예제들과 함께 알아봅시다.
1. 인터프리터에서 사용
파이썬 인터프리터에서 가장 마지막 표현식의 결과값을 자동적으로 "_"라는 변수에 저장합니다. 물론 여기 저장된 값을 다른데다가 저장할 수 있습니다.
일반적인 값처럼 쓸 수 있습니다.
>>> 5 + 4
9
>>> _
9
>>> _ + 6
15
>>> _
15
>>> a = _
>>> a
15
위와 같이 동작하는걸 볼 수 있습니다.
2. 무시하는 값
언더바는 무시하는 값으로도 쓰일 수 있습니다. 해당 값을 unpack하기 싫다면, 그냥 _에다가 할당하면 됩니다.
## 값을 버립니다.
a, _, b = (1, 2, 3) # a = 1, b = 3, _에 2가 할당됩니다.
print(a, b)
## 여러개 값 버리기
## *(변수) 는 unpack할때, 여러개의 값을 하나의 변수에 저장할때 쓰입니다.
## 이는 확장된 Unpacking이라고 불리며,Python 3.x 버전에서만 가능합니다.
a, *_, b = (7, 6, 5, 4, 3, 2, 1)
print(a, b)
테스트를 해보니, _에다가 할당하면 _도 일반 변수처럼 쓰이고, 1번처럼 Last expression을 저장하지 않게 됩니다. del _를 통해 해당 변수를 삭제하면 1번때처럼 Last expression을 저장하게 됩니다.
3. 루프에서 사용
for 루프를 돌 때 사용할 수 있습니다. 아래처럼 쓰는 것도 하나의 방법이 됩니다.
## _를 이용해서 루프를 돕니다.
for _ in range(5):
print(_)
## 리스트 순회를 _를 이용해서 합니다.
## _ 를 일반 변수처럼 사용할 수 있습니다.
languages = ["Python", "JS", "PHP", "Java"]
for _ in languages:
print(_)
_ = 5
while _ < 10:
print(_, end = ' ') # 'end'의 기본값은 '\n'인데 이걸 변경해줍니다.
_ += 1
실행결과는 예상한 것과 같게 아래처럼 나옵니다.
0
1
2
3
4
Python
JS
PHP
Java
5 6 7 8 9
4. 숫자값의 구분
숫자값이 길다면, 자릿수 구분을 위해 _를 중간중간에 넣어줄 수 있습니다.
이진수값이나 16진수, 8진수 값도 동일하게 적용할 수 있습니다.
## 여러 숫자 표현법
## 아래 값들이 정확한지 확인하기위해 int 함수를 써 볼수도 있습니다.
million = 1_000_000
binary = 0b_0010
octa = 0o_64
hexa = 0x_23_ab
print(million)
print(binary)
print(octa)
print(hexa)
실행 결과는 아래와 같습니다.
1000000
2
52
9131
5. 언더바를 포함한 변수명들
변수, 함수, 클래스 명 등에 언더바가 사용될 수 있습니다.
- 앞에 하나의 언더바 _variable
- 뒤에 하나의 언더바 variable_
- 앞에 둘의 언더바 __variable
- 앞과 뒤에 두개의 언더바 __variable__
5.1 앞의 하나의 언더바
앞에 하나의 언더바로 시작하는 이름은, 내부 사용용입니다(internal use only). 일단 예시부터 봅시다.
class Test:
def __init__(self):
self.name = "datacamp"
self._num = 7
obj = Test()
print(obj.name)
print(obj._num)
위 코드를 실행하면 아래처럼 됩니다.
datacamp
7
변수명 앞에 _를 하나 붙였다고 해서, 해당 변수를 접근하지 못하게 되진 않습니다. 하지만 모듈을 import해서 쓰는 경우 효과가 발생합니다.
아래와 같은 코드를 한번 확인해봅시다.
## filename:- my_functions.py
def func():
return "datacamp"
def _private_func():
return 7
이제 my_functions.py를 가져오기 위해 import 구문을 써보자. 파이썬은 언더바 하나로 시작한 이름들은 import하지 않는다.
>>> from my_functions import *
>>> func()
'datacamp'
>>> _private_func()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_private_func' is not defined
위의 코드 결과를 보다시피, _로 시작한 _private_func는 찾지를 못한다.
위와 같은 에러를 방지하기 위해, from module import *가 아닌 모듈 자체를 import를 해보자.
>>> import my_functions
>>> my_functions.func()
'datacamp'
>>> my_functions._private_func()
7
앞에 _ 1개로 시작한 이름은 내부 사용 전용이라는 뜻이다.
5.2 뒤의 하나의 언더바
가끔 파이썬 키워드에 해당하는 이름으로 변수명, 함수명, 클래스명으로 쓰고 싶을때가 있을 수 있다. 이럴때 이 방법이 유용하다. 마지막 부분에 언더바를 하나 추가함으로써 파이썬의 기본 키워드들과 충돌하는거를 방지할 수 있다.
>>> def function(class):
File "<stdin>", line 1
def function(class):
^
SyntaxError: invalid syntax
>>> def function(class_):
... pass
...
>>>
마지막에 하나의 언더바를 추가하는 명명법은 파이썬 키워드와 겹치는 경우를 방지하고 싶을때이다.
5.3 앞의 두개의 언더바
앞에 두개의 언더바로 시작하는 명명법은 name mangling이다. mangle은 짓이기다라는 뜻인데, 알아보기 힘든 모양으로 만든다라는 의미로 볼 수 있겠다.
앞에 두개의 언더바로 시작하는 명명법은 파이썬 인터프리터에게 해당 서브클래스의 attribute 이름을 바꾸어서 이름 충돌이 나지 않게 하라고 말하는 것이다. 서브클래스 이야기가 나온걸로 봐서 상속하는 경우와 관련이 있는 것 같다.
일단 예시를 보자.
class Sample():
def __init__(self):
self.a = 1
self._b = 2
self.__c = 3
obj1 = Sample()
dir(obj1)
dir한 결과는 아래와 같이 나온다.
['_Sample__c',
'__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__',
'_b',
'a']
dir함수는 클래스 객체의 모든 attribute들을 리턴해준다. 선언한 변수들을 dir 리턴값 중에서 한번 찾아보자.
a와 _b는 잘 보인다. 맨 마지막에 있다. _b는 내부 사용용으로만 쓰인다.
Sample클래스에 분명히 __c라는 맴버변수를 선언했는데, __c는 없고 대신 _Sample__c라는 값이 있다.
이것이 name mangline이다. 나중에 다른 클래스가 Sample이라는 클래스를 상속할때, 그 클래스에서 이 변수를 override하는 걸 방지해준다.
다른 클래스를 만들어서 Sample이라는 클래스를 상속해보자.
class SecondClass(Sample):
def __init__(self):
super().__init__()
self.a = "overridden"
self._b = "overridden"
self.__c = "overridden"
obj2 = SecondClass()
print(obj2.a)
print(obj2._b)
print(obj2.__c)
상속을 한 뒤, 초기화를 하는 과정에서 Sample의 맴버변수들에 값을 다 대입하고 있다.
overridden
overridden
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-2-4bf6884fbd34> in <module>()
9 print(obj2.a)
10 print(obj2._b)
---> 11 print(obj2.__c)
AttributeError: 'SecondClass' object has no attribute '__c'
__c를 찾지 못해서 에러가 발생했다. name mangline때문에 obj2.__c가 아닌 obj2.__SecondClass__c로 바뀌었다. obj2._SecondClass__c를 한번 출력해보자.
print(obj2._SecondClass__c)
overridden
편의상 결과도 같이 썼다.
그러면 _Sample__c는 어떻게 되었는지 한번 보자.
print(obj1._Sample__c)
3
mangle 된 이름으로 잘 있다.
맴버함수(메소드)를 이용해서 mangle 된 두개의 언더바로 시작하는 변수를 접근할 수 있다. 예시를 보자.
class SimpleClass:
def __init__(self):
self.__datacamp = "Excellent"
def get_datacamp(self):
return self.__datacamp
obj = SimpleClass()
print(obj.get_datacamp()) ## "Excellent"를 출력한다. __datacamp에 해당하는 값이다.
print(obj.__datacamp) ## 여기서 에러가 발생한다. 변수 이름이 바뀐다.
메소드(getter, setter)안에서는 잘 접근을 한다. 하지만 외부에서 멤버 변수로 직접 접근하려고 하면 에러가 난다.
언더바 2개로 시작하는 명명법은 변수뿐만 아니라 메소드 이름에도 적용이 가능하다. 예시를 보자.
class SimpleClass:
def __datacamp(self):
return "datacamp"
def call_datacamp(self):
return self.__datacamp()
obj = SimpleClass()
print(obj.call_datacamp()) ## __datacamp()의 리턴값과 같다.
print(obj.__datacamp()) ## 여기선 에러가 난다.
__로 시작한 함수는 안에서만 호출이 되는 용도라고 보면 될 것 같다.
다른 객체지향 언어에서 private access modifier(접근 지정자)의 역할이라고 보면 될 것 같다.
다른 예시를 한번 보자.
_SimpleClass__name = "datacamp"
class SimpleClass:
def return_name(self):
return __name
obj = SimpleClass()
print(obj.return_name()) ## "datacamp"를 출력하게 된다.
위와 같은 코드도 정상적으로 동작을 한다.
좀 특이한 방식의 개념이다.
5.4 앞뒤로 2개의 언더바
파이썬에서 앞뒤로 2개의 언더바로 둘러쌓인 명명법을 본 적이 있을 것이다. 이녀석들은 매직 메소드(magic method) 혹은 dunder 메소드라고 불린다.
class Sample():
def __init__(self):
self.__num__ = 7
obj = Sample()
obj.__num__
이 명명방식을 변수 이름으로 사용할 경우 변수명 충돌이 일어날 수 있기 때문에 가급적 삼가하는게 좋다.
dunder method에서 dunder는 double under(score)를 뜻하며, 보통 연산자 오버로딩을 할 때 많이 사용한다고 한다.
매직 메소드는 __init__, __add__, __len__, __repr__등이 있다.
__init__메소드는 생성자라고 보면된다. 호출없이 초기화할때 호출되는 함수이며, 클래스의 인스턴스가 생성될때, 실행되게 된다.
__repr__ 메소드는 해당 클래스를 출력하고자 할때 내부적으로 호출되는 메소드라고 보면 된다. 자바로 치면 toString() 함수와 유사하다고 볼 수 있겠다.
References
'개발 & CS 지식' 카테고리의 다른 글
데이터베이스 종류와 SQL 그리고 스키마(schema) (0) | 2020.09.22 |
---|---|
엑셀(스프레드시트) vs 데이터베이스, 데이터 베이스란? (0) | 2020.09.21 |
Ubuntu 에서 Virtualbox 제대로 동작 안하는 경우 해결 (0) | 2020.08.21 |
리눅스에서 python3을 지우면 안되는 이유, 복구법 (0) | 2020.08.12 |
코딩이란 무엇일까? (0) | 2020.07.24 |