파이썬 코드들을 보다보면 아래와 같은 코드를 종종 볼 수 있습니다.
if __name__ == '__main__':
# something here
이 코드가 하는 일은 무엇일까요? 오늘은 이 코드의 작동 원리와 언제 사용하는지 알아보겠습니다.
__name__ 은 무엇일까요?
먼저 첫번째 퍼즐 조각인 __name__
이 무엇인지부터 알아보겠습니다.
Python에는 미리 정의된 특수한 함수나 변수들이 있습니다. 함수는 __do()__
같은 형태이고, 변수는 __val__
같은 형태입니다. 이런 특수한 함수와 변수는 보통 Python 내부적으로 사용하기 위한 것들이라서 코딩 때 직접 사용할 일은 그다지 많지 않습니다. 그런데 __name__
은 아주 유용한 특성이 있어서 일반 코딩 때도 곧잘 사용되곤 합니다. 좋은 것들은 잘 써야지요 :)
Python Interpreter가 코드를 처음 읽을 때 몇가지 동작을 하는데 그 중 하나가 바로 __name__
변수를 정의하고 값을 채우는 것입니다. 그렇다면 Python Interpreter가 코드를 읽는 때는 언제일까요? 1) 다른 코드가 해당 파일을 import
하거나 2) Python Interpreter가 해당 파일을 직접 실행 할 때입니다.
실제 동작을 알아보기 위해서 아래와 같은 코드를 my_module.py
라고 저장합니다.
def useful_task():
print("I am a very useful function!")
print('__name__:', __name__)
함수 하나를 선언하고, __name__
의 값을 출력하는 코드입니다.
코드가 import 될 때 __name__
import 될 때 동작으르 보기 위해서 main.py
파일을 만들고 아래 내용을 작성해보겠습니다.
import my_module
my_module.useful_task()
>> python3 main.py
__name__: my_module
I am a very useful function!
첫번째 줄이 my_module.py
가 import 되면서 실행된 결과입니다. __name__
의 값이 my_module
이라는 것을 알 수 있습니다. Python은 코드를 import 할 때 기본적으로 파일의 이름을 __name__
에 저장합니다. 이 경우에는 my_module
입니다.
Python Interpreter가 코드를 실행할 때
코드가 직접 실행될 때 동작을 보기위해 Shell에서 아래와 같이 명령을 내려보겠습니다.
>> python3 my_module.py
__name__: __main__
두번째 퍼즐 조각인 __main__
이 나왔습니다. 코드를 import 할 때와는 다르게 Python Interpreter가 코드를 직접 실행할 때는 __name__
의 값을 __main__
으로 채웁니다. 실제 파일명과는 무관합니다.
그렇다면 이 코드가 하는 일은 무엇일까요?
아래와 같이 코드를 바꿔보겠습니다.
먼저 my_moduel.py
입니다.
def useful_task():
print("I am a very useful function!")
if __name__ == '__main__':
useful_task()
print("I can do another useful task as well!")
다음으로 main.py
입니다.
import my_module
my_module.useful_task()
print("Stupid but fun task.")
main.py
실행
이제 main.py
를 실행해보겠습니다.
>> python3 main.py
I am a very useful function!
Stupid but fun task.
main.py
는 my_module
을 import 하고 있기 때문에 my_module.py
입장에서 __name__
의 값은 my_module
입니다. 즉, __name__ == '__main__'
은 거짓이 되고 그 아래 코드들은 실행되지 않습니다.
my_module.py
실행
이번에는 my_module.py
를 실행해보겠습니다.
>> python3 my_module.py
I am a very useful function!
I can do another useful task as well!
my_module.py
를 직접 실행했기 때문에 my_module.py
입장에서 __name__
의 값은 __main__
이 됩니다. 이번에는 __name__ == '__main__'
은 참이 되고 그 아래 코드들이 실행됩니다.
중간 정리
앞에서 본대로 if __name__ == '__main__':
을 사용하면 파일이 import 될 때와 직접 실행될 때 실행될 코드를 제어할 수 있습니다. import 되든 직접 실행되든 관계없이 공통으로 실행되야하는 부분은 if __name__ == '__main__':
바깥에 두고, 직접 실행할때만 필요한 코드는 if __name__ == '__main__':
안에 넣으면 됩니다.
여기까지만이면 너무 심심하죠. 실전에서 쓰는 법을 알아보겠습니다
언제 이 표현을 쓸까요?
그렇다면 실전에서는 어떻게 사용할까요?
제가 사진에 강아지가 있다면 그 사진에서 강아지 부분을 표시히고 강아지의 종을 맞추는 프로그램을 만들고 있다고 가정해보겠습니다. 그리고 파일 이름은 identify_dog.py
라고 해보겠습니다. 슈도 코드로 만들어 보자면 아래와 같은 모양일 것입니다.
def has_a_dog(image):
pass
def mark_a_dog(image):
pass
def predict_a_breed(image):
pass
if has_a_dog(image):
mark_a_dog(image)
predict_a_breed(image)
이 자체로 좋은 프로그램이지만 생각해보니 has_a_dog()
, mark_a_dog()
, predict_a_breed()
같은 함수는 다른 곳에서도 유용하게 쓸일 것 같습니다. 그래서 identify_dog.py
를 라이브러리(모듈)로 배포하기로 결심합니다.
하지만 문제가 있습니다.
import identify_dog
identify_dog.has_a_dog(image)
이라고 쓰고 싶은데 그렇게하면 if has_a_dog(image):
와 그 아래 부분까지 실행됩니다. 라이브러리를 쓰는 입장에서는 불필요할 뿐만 아니라 오류를 발생시키는 것이죠.
한 가지 방법은 코드를 복사해서 두가지 배포본을 만드는 것입니다.
라이브러리용으로는 identify_dog_lib.py
def has_a_dog(image):
pass
def mark_a_dog(image):
pass
def predict_a_breed(image):
pass
실행용으로는 identify_dog_script.py
def has_a_dog(image):
pass
def mark_a_dog(image):
pass
def predict_a_breed(image):
pass
if has_a_dog(image):
mark_a_dog(image)
predict_a_breed(image)
똑같은 코드가 여러곳에 복사되어 있기 때문에 여러가지 문제가 있을 수 있습니다. 좋지 않은 방식입니다.
해결책은 if __name__ == '__main__':
을 사용하는 것입니다. indentify_dog.py
를 아래와 같이 만듭니다.
def has_a_dog(image):
pass
def mark_a_dog(image):
pass
def predict_a_breed(image):
pass
if __name__ == '__main__':
if has_a_dog(image):
mark_a_dog(image)
predict_a_breed(image)
이제 identify_dog.py
를 직접 실행하면 if has_a_dog(image):
와 그 아래 부분까지 실행되고, import를 한다면 그 앞까지만 실행됩니다.
부적절한 사용
이 방법을 사용하지 않아야할 때도 있을까요? 개인적으로 if __name__ == '__main__':
아래에는 진짜 실행을 위한 코드를 넣어야한다고 생각합니다. 작성하고 있는 모듈이 잘 작동하는지 테스트를 하기 위해서 if __name__ == '__main__':
아래에 테스트용 코드를 넣는 경우도 있는데요. 테스트를 위해서는 이 모듈을 import 하는 별도의 테스트 파일을 만들거나 test framework를 사용하는 것이 좋다고 생각합니다.
마무리
이번 포스트에서는 Python이 __name__
을 어떻게 다루는지를 알아보았습니다. 그리고 이를 이용해 파일이 모듈로 import 될 때와 직접 실행될 때 동작을 다르게 할 수 있는 방법을 알아보았습니다.
실제 많은 Python 모듈들이 이 방식을 사용해서 모듈과 실행 스크립트의 역할을 모두 수행하고 있습니다.
'Python' 카테고리의 다른 글
itertools: iterator를 위한 도구 모음 (2) | 2023.02.19 |
---|---|
Python GIL(Global Interpreter Lock)이 무엇일까요? (0) | 2023.01.13 |
Python Callable (0) | 2022.07.11 |
Python 메소드의 첫번째 인자인 `self`는 무엇인가? (0) | 2022.03.24 |
Iterator, Generator (0) | 2021.05.08 |