파이썬 소스코드를 보다보면 아래와 같이 if __name__ == "__main__": 구문을 많이 볼 수 있습니다.

if __name__ == "__main__":
    # Execute when the module is not initialized from an import statement.
    main()

위 문장이 어떤 의미인지 알아볼까요.

 

 __name__이란?

모듈의 이름을 담고 있는 파이썬 내장 변수입니다.

python.py 라는 파일이 있다면 __name__는 .py 확장자를 제외한 python이 됩니다.

 

__main__이란?

__main__은 최상위 코드가 실행되는 환경의 이름입니다. "최상위 코드"는 프로그램 실행 시 첫 번째로 실행되는 Python 모듈입니다. 프로그램 구동에 필요한 다른 모듈들을 가져오기(import) 때문에 "최상위"라고 합니다.

"최상위 코드"를 애플리케이션의 진입점(entry point)이라고도 합니다.

모듈은 __name__을 확인하여 최상위 환경에서 실행 중인지의 여부를 확인할 수 있습니다

모듈이 최상위 코드 환경에서 실행되면 해당 __name__은 '__main__' 문자열로 설정됩니다.

Python 모듈 또는 패키지를 imort하면 __name__은 모듈 이름으로 설정됩니다. 일반적으로 .py 확장자가 없는 파일 이름입니다.

 

<예제>

모듈이 메인 프로그램인 경우

first_module.py 파일을 만들고 아래와 같이 입력합니다.

##  first_module.py
print('__name__ value :', __name__)

아래와 같이 python first_module.py를 명령창에서 수행하면 __name__ 값이 '__main__'인 것을 알 수 있습니다. 이는 위에서 설명한 최상위 코드(main program)에서 실행했기 때문입니다.

$ python first_module.py
__name__ value : __main__

즉, 파이썬 인터프리터는 문자열 '__main__'을 __name__변수에 할당합니다.

# It's as if the interpreter inserts this at the top
# of your module when run as the main program.
__name__ = "__main__" 

 

모듈을 import하는 경우

다른 모듈이 위에서 작성한 모듈을 import 할 경우입니다. second_module.py 파일을 아래와 같이 생성합니다.

# second_module.py
import first_module

print('__name__ value :', __name__)

아래와 같이 python second_module.py를 명령창에서 수행하면 import한 first_module의 __name__ 값이 'first_module'인 것을 알 수 있습니다. 이는 위에서 설명한대로 .py 확장자를 제외한 파일명으로 설정됩니다. 

$ python second_module.py
__name__ value : first_module
__name__ value : __main__

파이썬 인터프리터는 first_module.py 파일을 검색하고 해당 모듈을 실행하기 전에 import  name문에서 __name__변수로 모듈 이름(확장자 .py를 제외한 이름)을 지정합니다.

# It's as if the interpreter inserts this at the top
# of your module when it's imported from another module.
__name__ = "first_module"

 

위의 예제에서처럼 import를 하게되면 import된 모듈이 실행이 됩니다. 위와같이 의도치 않게 import module이 실행되는 것을 막기 위해 

if __name__ == "__main__": 구문을 사용합니다.

즉, 메인 프로그램에서 실행했을 경우에만 수행을 하도록 제어하는 것입니다.

 

위에서 사용한 first_module.py과 second_module.py 파일을 아래와 같이 수정합니다.

##  first_module.py
if __name__ == "__main__": 
	print('__name__ value :', __name__)
# second_module.py
import first_module

if __name__ == "__main__": 
	print('__name__ value :', __name__)

아래와 같이 다시 second_module.py 를 실행합니다. first_module은 최상위 코드가 아니므로 if __name__ == "__main__":  구문에 의해 print 문장이 수행되지 않았습니다.

$ python second_module.py
__name__ value : __main__

이와같은 사유로 if __name__ == "__main__": 

구문을 습관적으로 사용해야 합니다.

 

가장 기본적인 사항만 설명하였으니, 상세 내용은 아래를 참조하세요. __name__과 __main__ 관련하여 위에서 설명한 것 이외에도 많은 내용들이 있습니다.

 

아래 내용은 파이썬 document의 내용입니다.

내용이 많고 복잡합니다.

https://docs.python.org/3/library/main.html?highlight=main

 

 

__main__ — Top-level code environment — Python 3.10.1 documentation

Both of these mechanisms are related to Python modules; how users interact with them and how they interact with each other. They are explained in detail below. If you’re new to Python modules, see the tutorial section Modules for an introduction. __name_

docs.python.org

Python에서 __main__이라는 이름은 다음 두 가지 구성에 사용됩니다.

  • 프로그램의 최상위 코드 환경 이름으로,  __name__ == '__main__' 표현식
  • Python 패키지의 __main__.py 파일

Python 모듈 또는 패키지를 가져올 때 __name__은 모듈의 이름으로 설정됩니다.

모듈이 최상위 코드 환경에서 실행되면 해당 __name__이 '__main__' 문자열로 설정됩니다.

 

Python 모듈 또는 패키지를 가져오면 __name__이 모듈 이름으로 설정됩니다. 일반적으로 .py 확장자가 없는 Python 파일 자체의 이름입니다.

 

"최상위 코드 환경"이란?
__main__은 최상위 코드가 실행되는 환경의 이름입니다. "최상위 코드"는 실행을 시작하는 첫 번째 Python 모듈입니다. 프로그램에 필요한 다른 모든 모듈을 가져오기 때문에 "최상위"라고 합니다. "최상위 코드"를 애플리케이션의 진입점이라고도 합니다.

 

결과적으로 모듈은 __name__을 확인하여 최상위 환경에서 실행 중인지 여부를 확인할 수 있습니다. 이는 모듈이 import 문에서 초기화되지 않을 때 조건부로 코드를 실행하는 일반적인 관용구를 허용합니다.

if __name__ == '__main__':
    # Execute when the module is not initialized from an import statement.
    ...

관용적 사용법
일부 모듈에는 명령줄 인수를 구문 분석하거나 표준 입력에서 데이터를 가져오는 것과 같이 스크립트 전용 코드가 포함되어 있습니다. 예를 들어 단위 테스트를 위해 이와 같은 모듈을 다른 모듈에서 가져온 경우 스크립트 코드도 의도하지 않게 실행됩니다.

여기서 if __name__ == '__main__' 코드 블록을 사용하면 편리합니다. 이 블록 내의 코드는 모듈이 최상위 환경에서 실행되지 않는 한 실행되지 않습니다.

if __name__ == '__main__' 아래 블록에 가능한 한 적은 수의 명령문을 넣으면 코드 명확성과 정확성이 향상될 수 있습니다. 대부분의 경우 main이라는 함수는 프로그램의 기본 동작을 캡슐화합니다. 

# echo.py

import shlex
import sys

def echo(phrase: str) -> None:
   """A dummy wrapper around print."""
   # for demonstration purposes, you can imagine that there is some
   # valuable and reusable logic inside this function
   print(phrase)

def main() -> int:
    """Echo the input arguments to standard output"""
    phrase = shlex.join(sys.argv)
    echo(phrase)
    return 0

if __name__ == '__main__':
    sys.exit(main())  # next section explains the use of sys.exit

 

모듈이 main 함수 내부에 코드를 캡슐화하지 않고 대신 if __name__ == '__main__' 블록 내에 직접 넣는 경우, phrase 변수는 전체 모듈에 대해 global 변수가 됩니다. 모듈 내의 다른 함수가 의도하지 않게 local 변수 대신 global 변수를 사용할 수 있으므로 오류가 발생하기 쉽습니다. main 함수는 이 문제를 해결합니다.

main 함수를 사용하면 echo 함수 자체가 격리되고 다른 어느 곳에서나 import할 수 있다는 이점이 있습니다.

echo.py를 가져오면 echo와 main 함수가 정의되지만 둘 다 호출되지 않습니다.

왜냐하면 __name__ != '__main__' 때문입니다.

 

패키징 고려 사항
주요 기능은 콘솔 스크립트의 진입점으로 지정하여 명령줄 도구를 만드는 데 자주 사용됩니다. 이 작업이 완료되면 pip는 main의 반환 값이 sys.exit()에 전달되는 템플릿 스크립트에 함수 호출을 삽입합니다. 예를 들어:

sys.exit(main())
main에 대한 호출은 sys.exit()에 래핑되어 있으므로 함수가 sys.exit()에 대한 입력으로 허용되는 일부 값을 반환할 것입니다. 일반적으로 정수 또는 None(함수에 return 문이 없는 경우 암시적으로 반환됨)입니다.

이 규칙을 따랐다면 모듈은 나중에 pip-installable 패키지의 콘솔 스크립트 진입점으로 패키징할 때와 마찬가지로 직접 실행할 때(예: python3 echo.py) 동일한 동작을 갖게 됩니다.

특히 main 함수에서 문자열을 반환할 때 주의하십시오. sys.exit()는 문자열 인수를 실패 메시지로 해석하므로 프로그램은 실패를 나타내는 종료 코드 1을 가지며 문자열은 sys.stderr에 기록됩니다. 

 

__main__.py in Python Packages

일반적으로 __main__.py 파일은 패키지에 대한 명령줄 인터페이스를 제공하는 데 사용됩니다.

다음 패키지 "bandclass"를 가정해봅니다.

bandclass
  ├── __init__.py
  ├── __main__.py
  └── student.py

__main__.py는 패키지 자체가 -m 플래그를 사용하여 명령줄에서 직접 호출될 때 실행됩니다. 예를 들어:

$ python3 -m bandclass

이 명령은 __main__.py가 실행되도록 합니다. 이 메커니즘을 사용하는 방법은 작성 중인 패키지의 특성에 따라 다르지만 이 경우 교사가 학생을 검색하도록 허용하는 것이 합리적일 수 있습니다.

# bandclass/__main__.py

import sys
from .student import search_students

student_name = sys.argv[2] if len(sys.argv) >= 2 else ''
print(f'Found student: {search_students(student_name)}')

from .student import search_students는 상대적 import의 예입니다. 이 import 스타일은 패키지 내에서 모듈을 참조할 때 사용할 수 있습니다.

 

관용적 용법

__main__.py의 내용은 일반적으로 if __name__ == '__main__' 블록으로 구분되지 않습니다. 대신 해당 파일은 다른 모듈에서 실행하기 위해 짧게 유지됩니다. 그런 다음 이러한 다른 모듈은 단위 테스트를 쉽게 수행할 수 있으며 적절하게 재사용할 수 있습니다.

if __name__ == '__main__' 블록을 사용하면 패키지 내의 __main__.py 파일에 대해 예상대로 작동합니다. import가 되면 __name__ 속성에 패키지의 경로가 포함되기 때문입니다.

>>>
>>> import asyncio.__main__
>>> asyncio.__main__.__name__
'asyncio.__main__'

이것은 .zip 파일의 루트 디렉토리에 있는 __main__.py 파일에는 작동하지 않습니다. 따라서 일관성을 위해 위에서 언급한 venv와 같은  __main__.py가 더 좋습니다.

 

import __main__

Python 프로그램이 시작된 모듈에 관계없이 동일한 프로그램 내에서 실행되는 다른 모듈은 __main__ 모듈을 가져와서 최상위 환경의 범위(네임스페이스)를 가져올 수 있습니다. 이것은 __main__.py 파일을 가져오는 것이 아니라 특별한 이름 '__main__'을 받은 모듈을 가져옵니다.

다음은 __main__ 네임스페이스를 사용하는 예제 모듈입니다.

 

Here is an example module that consumes the __main__ namespace:

# namely.py

import __main__

def did_user_define_their_name():
    return 'my_name' in dir(__main__)

def print_user_name():
    if not did_user_define_their_name():
        raise ValueError('Define the variable `my_name`!')

    if '__file__' in dir(__main__):
        print(__main__.my_name, "found in file", __main__.__file__)
    else:
        print(__main__.my_name)

이 모듈의 사용 예는 다음과 같습니다

# start.py

import sys

from namely import print_user_name

# my_name = "Dinsdale"

def main():
    try:
        print_user_name()
    except ValueError as ve:
        return str(ve)

if __name__ == "__main__":
    sys.exit(main())

 

프로그램을 실행하면 결과는 다음과 같습니다.

$ python3 start.py
Define the variable `my_name`!

 

프로그램의 종료 코드는 오류를 나타내는 1이 됩니다. my_name = "Dinsdale"이 있는 줄의 주석 처리를 제거하면 프로그램이 수정되고 이제 성공을 나타내는 상태 코드 0으로 종료됩니다

$ python3 start.py
Dinsdale found in file /path/to/start.py

__main__을 가져오면 시작 모듈의 if __name__ == "__main__" 블록에 있는 스크립트 사용을 위한 최상위 코드를 의도하지 않게 실행하는 데 문제가 발생하지 않습니다. 왜 이런 식으로 작동할까요?

Python은 인터프리터 시작 시 sys.modules에 빈 __main__ 모듈을 삽입하고 최상위 코드를 실행합니다.

이 예에서는 한 줄씩 실행하고 namely를 import하는 시작 모듈입니다. 차례로 __main__(실제로 시작됨)을 import합니다.

이것이 import cycle입니다.

부분적으로 채워진 __main__ 모듈이 sys.modules에 있기 때문에 Python은 이를 namely로 전달합니다. 

Python REPL은 "최상위 환경"의 또 다른 예이므로 REPL에 정의된 모든 것이 __main__ 범위의 일부가 됩니다.

>>>
>>> import namely
>>> namely.did_user_define_their_name()
False
>>> namely.print_user_name()
Traceback (most recent call last):
...
ValueError: Define the variable `my_name`!
>>> my_name = 'Jabberwocky'
>>> namely.did_user_define_their_name()
True
>>> namely.print_user_name()
Jabberwocky

이 경우 __main__ 범위는 대화형이므로 __file__ 속성을 포함하지 않습니다.
__main__ 범위는 pdb 및 rlcompleter의 구현에 사용됩니다.

 

파이썬은 왜 프로그램의 시작점이 정해져 있지 않나요?

파이썬이 처음에 개발 될 당시에는 리눅스/유닉스에서 사용하는 스크립트 언어 기반이었기 때문에 프로그램의 시작점이 따로 정해져 있지 않았습니다. 보통 리눅스/유닉스의 스크립트 파일은 파일 한 개로 이루어진 경우가 많은데, 이 스크립트 파일 자체가 하나의 프로그램이다 보니 시작점이 따로 필요하지 않습니다. 하지만 C 언어나 자바같은 언어는 처음 만들어질 때부터 소스 파일을 여러 개 사용했기 때문에 여러 소스 파일의 함수들 중에서도 시작 함수(main)를 따로 정해 놓았습니다.

+ Recent posts