파이썬 프로그래밍 (4) - 모듈/패키지/프로젝트 구조 분석

이영락·2024년 8월 22일

개발자 기본기

목록 보기
4/53

모듈 /패키지/ 프로젝트 구조 분석

목차

  1. 모듈의 구성
  2. 모듈의 구성 예시
  3. 모듈 main.py 또는 run.py의 역할
  4. 패키지(Package) 구조
  5. 프로젝트 구조
  6. 개발 시 고려 사항
  7. __name__ == "__main__"

🏖️ 모듈 구조

모듈의 구성

: 모듈은 파이썬 코드가 포함된 .py 파일.

  • 함수(def), 클래스(class), 변수 정의, 실행 가능한 코드 등 다양한 요소들을 포함할 수 있다.
  • 모듈의 목적은 코드의 재사용과 조직화를 돕는 것.

• 네임스페이스(Namespace): 모듈을 호출할 때, 모듈 내부의 함수나 클래스가 독립적인 범위(네임스페이스)를 가지게 됩니다. 이를 통해 다른 모듈의 함수와 이름이 겹치더라도 충돌 없이 사용할 수 있습니다.

모듈의 구성 예시

  • 함수 정의: 주로 여러 곳에서 재사용할 수 있는 함수들을 정의합니다.

    # utils.py
    def add(a, b):
        return a + b
    
    def subtract(a, b):
        return a - b
  • 클래스 정의: 객체지향 프로그래밍을 위해 클래스도 정의할 수 있습니다.

    # models.py
    class Calculator:
        def __init__(self):
            self.result = 0
    
        def add(self, value):
            self.result += value
    
        def subtract(self, value):
            self.result -= value
  • 변수 정의: 상수나 초기 설정 값을 저장해둘 수 있습니다.

    # config.py
    PI = 3.14159
    MAX_CONNECTIONS = 10

comment! : 변수의 경우 패키지 내에서 __init__.py 파일내에 변수를 선언하여 패키지 전체에서 활요할 수 있도록 하는 것이 좋다.

  • 실행 가능한 코드: 모듈 내에서 특정 코드가 실행되도록 할 수도 있지만, 이 경우 보통 if __name__ == "__main__": 구문을 사용해 해당 모듈이 직접 실행될 때만 코드를 실행하도록 합니다.

    # script.py
    def greet(name):
        return f"Hello, {name}!"
    
    if __name__ == "__main__":
        print(greet("World"))

모듈 main.py 또는 run.py의 역할

: main.pyrun.py 같은 파일은 프로젝트의 진입점(엔트리 포인트)으로 사용된다.
이 파일에서는 여러 모듈을 불러와서 조합하여 프로그램을 실행하는 역할을 한다.

예시 프로젝트 구조

my_project/
├── my_package/
│   ├── __init__.py
│   ├── utils.py
│   └── models.py
└── main.py

main.py에서 모듈 사용 예시

# main.py
from my_package.utils import add, subtract
from my_package.models import Calculator

def main():
    print("Simple Math Operations")
    print(f"2 + 3 = {add(2, 3)}")
    print(f"5 - 2 = {subtract(5, 2)}")

    calc = Calculator()
    calc.add(10)
    calc.subtract(3)
    print(f"Calculator result: {calc.result}")

if __name__ == "__main__":
    main()

이러한 구조에서 main.py는 프로그램의 흐름을 관리하며, 필요한 모듈을 불러와 사용합니다. 이로 인해 코드를 더 잘 조직화할 수 있고, 재사용 가능성을 높일 수 있습니다. 모듈 자체는 특정 기능을 제공하고, 실행 자체는 main.py와 같은 엔트리 포인트에서 이루어지는 것이 일반적인 패턴입니다.

comment! : 각 기능내의 세부 구조들을 모듈로 구성하고 그 모듈의 작동을 위한 모듈로 main.py or run.py로 만든다.


🏖️ 패키지 구조

패키지(Package)

정의: 패키지는 여러 모듈을 하나로 묶은 단위입니다.

  • 디렉토리(폴더)로 표현되며, 각 디렉토리에는 __init__.py 파일이 존재해야 패키지로 인식됩니다.
  • __init__.py 파일은 패키지 초기화에 필요한 코드를 담고 있을 수 있습니다.

패키지 구조

  • 패키지 구조 예시:

    my_project/
    ├── my_package/
    │   ├── __init__.py
    │   ├── module1.py
    │   └── module2.py
    └── main.py
  • 모듈 사용 예시:

    # main.py
    from my_package.module1 import some_function
    from my_package.module2 import another_function
    
    some_function()
    another_function()
    • 상대 참조와 절대 참조: 패키지 내에서 모듈을 불러올 때, 다른 모듈을 참조할 수 있습니다. .은 현재 디렉토리, ..은 부모 디렉토리를 나타내며 상대 경로 참조를 할 수 있습니다. 절대 참조는 패키지의 전체 경로를 명시하여 참조하는 방법입니다.

🏖️ 프로젝트 구조

프로젝트 구조의 예

  • 일반적으로 파이썬 프로젝트는 다음과 같은 구조를 가집니다:
    my_project/
    ├── my_package/
    │   ├── __init__.py
    │   ├── module1.py
    │   └── module2.py
    ├── tests/
    │   ├── __init__.py
    │   ├── test_module1.py
    │   └── test_module2.py
    ├── venv/ (가상환경)
    ├── requirements.txt (프로젝트 의존성 관리)
    └── main.py (프로젝트 시작점)

🏖️ 개발 시 고려 사항**

  • 모듈화: 프로젝트를 작은 모듈로 나누어, 각 모듈이 독립적으로 작동하면서도 상호 작용할 수 있도록 합니다.
  • 재사용성: 잘 구조화된 모듈과 패키지는 다른 프로젝트에서도 재사용할 수 있습니다.
  • 가상환경 사용: 프로젝트별로 필요한 패키지를 독립적으로 관리함으로써 개발 환경을 깨끗하게 유지합니다.

🏖️ __name__ == "__main__"

__name__ == "__main__" 구문이 왜 직접 실행될 때만 작동하는지 이해하는가?
파이썬이 스크립트나 모듈을 실행할 때 어떻게 처리하는지 세부적으로 분석해보자.

1. 파이썬 스크립트 실행 과정

파이썬에서 스크립트를 실행하거나 모듈을 임포트할 때, 파이썬 인터프리터는 특정한 방식으로 해당 파일을 처리합니다.

a. 직접 실행

  • 파일을 직접 실행할 때, 예를 들어 python script.py라고 실행하면, 파이썬 인터프리터는 이 파일을 최상위 레벨의 스크립트로 취급합니다.
  • 이 경우, 파이썬은 해당 파일을 "메인 프로그램"으로 인식하고, 이 파일의 모든 코드를 실행하기 전에 몇 가지 준비를 합니다.
  • 중요한 부분 중 하나는 __name__ 변수를 설정하는 것입니다. 이 변수를 "__main__"으로 설정하여, 이 파일이 메인 프로그램임을 명시합니다.

b. 임포트된 경우

  • 반대로, 다른 모듈에서 해당 파일을 임포트할 때, 예를 들어 import script라고 하면, 파이썬 인터프리터는 이 파일을 모듈로 인식합니다.
  • 파이썬은 이 파일을 읽고 실행하기 전에, __name__ 변수를 파일의 이름(모듈 이름)으로 설정합니다. 여기서 파일 이름은 script.py라면 "script"가 됩니다.

2. __name__ 변수의 역할

__name__ 변수는 파이썬에서 모듈(파일)의 이름을 저장하는 데 사용되는 내장 변수입니다. 이 변수는 모듈이 어떻게 실행되는지에 따라 다르게 설정됩니다.

  • 직접 실행: __name__ 변수는 "__main__"으로 설정됩니다. 이는 파이썬 인터프리터가 이 파일을 직접 실행한다는 것을 의미합니다.
  • 임포트될 때: __name__ 변수는 모듈의 이름으로 설정됩니다. 이는 파일이 다른 파일에서 임포트되어 사용된다는 것을 의미합니다.

__name__은 파이썬에서 모든 모듈에 기본적으로 정의되는 내장 변수입니다. 이 변수는 해당 모듈(파일)이 어떻게 실행되는지에 따라 다르게 설정되며, 파이썬 프로그램의 실행 흐름을 이해하고 제어하는 데 중요한 역할을 합니다.

__name__ 변수의 정의

  • __name__: 파이썬 인터프리터가 각 모듈을 실행할 때, 자동으로 생성되고 설정되는 특수한 내장 변수입니다. 이 변수는 문자열로 저장되며, 그 모듈의 이름을 담고 있습니다.

__name__ 변수의 역할

__name__ 변수는 코드가 어디서 어떻게 실행되고 있는지를 파악할 수 있게 해줍니다. 이를 통해 파이썬에서는 다양한 방식으로 코드를 재사용하고, 실행 흐름을 제어할 수 있습니다.

  • 모듈 테스트: 개발자가 모듈을 독립적으로 테스트할 수 있도록 돕습니다. if __name__ == "__main__": 구문을 사용해 모듈을 직접 실행할 때와 임포트할 때의 동작을 구분할 수 있습니다.
  • 조건부 코드 실행: 특정 코드가 모듈이 직접 실행될 때만 실행되도록 설정할 수 있습니다. 이는 코드 재사용성을 높이고, 불필요한 코드 실행을 방지합니다.

. __name__의 활용 사례

a. 모듈화와 재사용성

  • __name__을 사용하여 코드의 모듈화를 촉진하고, 동일한 파일을 여러 곳에서 사용할 수 있도록 합니다. 특정 코드 블록이 직접 실행될 때와 모듈로서 임포트될 때 다르게 동작하게 할 수 있습니다.

b. 단위 테스트

  • 모듈 내부에 단위 테스트 코드를 포함시키고, 이 코드가 해당 모듈이 직접 실행될 때만 동작하게 할 수 있습니다. 이 방법은 모듈을 개발하고 디버깅하는 데 유용합니다.

c. 스크립트와 라이브러리의 구분

  • 동일한 파일을 스크립트로도, 라이브러리로도 사용할 수 있도록 __name__을 활용합니다. 파일이 메인 프로그램으로 실행될 때는 스크립트로 동작하고, 임포트될 때는 라이브러리로 동작하도록 설정할 수 있습니다.

__name__의 동작 원리

파이썬이 모듈을 처리할 때, 가장 먼저 그 모듈을 읽고 파싱한 후, __name__ 변수를 설정합니다. 이 변수는 프로그램이 어떻게 실행되고 있는지를 파악하는 기준으로 작용하며, 조건부로 코드가 실행되도록 제어할 수 있습니다.

__name__과 관련된 기타 특수 변수

  • __file__: 현재 파일의 경로를 나타냅니다.
  • __package__: 현재 모듈이 속한 패키지의 이름을 나타냅니다. 패키지의 서브모듈을 임포트할 때 유용합니다.
  • __doc__: 모듈의 도큐멘테이션 문자열을 나타냅니다.

그렇다면 __name__을 직접 설정할 수 있는가?

__name__ 변수를 직접 설정할 수 없는 이유

__name__ 변수는 파이썬의 실행 환경을 기반으로 설정되기 때문에, 코드를 작성하는 시점에서 이 변수를 직접 변경하거나 설정할 수 없습니다. 이 변수는 파이썬 인터프리터의 실행 컨텍스트에 의해 결정되며, 코드 내에서 직접적인 할당이나 변경은 의미가 없습니다.

어떻게 __name__의 값을 "제어"할 수 있는가?

비록 __name__ 변수를 직접 설정할 수는 없지만, __name__의 값을 조건문을 통해 활용하여 코드의 실행 흐름을 제어할 수 있습니다. 예를 들어, 특정 코드가 모듈이 직접 실행될 때만 실행되도록 하거나, 임포트될 때는 다른 동작을 수행하도록 만들 수 있습니다.

임포트 시의 대체 방법: 모듈을 객체로 임포트하기

__name__ 값을 직접적으로 제어할 수는 없지만, 모듈을 동적으로 임포트하여 다른 이름으로 사용할 수 있는 방법은 있습니다. 이를 위해 파이썬의 importlib 모듈을 사용할 수 있습니다.

그렇다면 __name__을 직접 설정할 수 있는가? 에 대한 정리

  • __name__은 자동으로 설정되는 내장 변수로, 모듈의 실행 방식에 따라 그 값이 결정됩니다.
  • 직접 설정할 수는 없지만, importlib 등을 사용해 모듈을 동적으로 임포트할 수 있습니다.
  • __name__ 변수를 통해 코드의 실행 흐름을 제어할 수 있으며, 이를 활용하여 모듈이 어떻게 동작할지 결정할 수 있습니다.

결론적으로, __name__ 변수는 파이썬의 실행 컨텍스트에 따라 자동으로 설정되며, 이를 직접 변경하거나 설정하는 방법은 없습니다. 다만, 파이썬의 모듈화된 코드를 보다 유연하게 다루기 위해 __name__ 변수를 효과적으로 사용할 수 있습니다.

3. if __name__ == "__main__"의 작동 원리

이제 위의 설명을 바탕으로 if __name__ == "__main__": 구문의 작동 원리를 살펴봅니다:

a. 직접 실행의 경우

  • 상황: 파일이 직접 실행됩니다. (예: python script.py)
  • __name__: 이 파일은 메인 프로그램으로 간주되므로 __name__"__main__"으로 설정됩니다.
  • 결과: if __name__ == "__main__": 구문이 True가 되므로, 이 조건문 아래의 코드가 실행됩니다.

b. 임포트될 경우

  • 상황: 파일이 다른 파일에서 임포트됩니다. (예: import script)
  • __name__: 이 파일은 모듈로 간주되므로 __name__"script"로 설정됩니다.
  • 결과: if __name__ == "__main__": 구문이 False가 되므로, 이 조건문 아래의 코드는 실행되지 않습니다.

4. 예제 코드 분석

# script.py
def my_function():
    print("Hello from my_function!")

if __name__ == "__main__":
    print("script.py is being run directly")
    my_function()

a. 직접 실행

$ python script.py
  • __name__: "__main__"
  • 출력:
    script.py is being run directly
    Hello from my_function!

여기서, if __name__ == "__main__": 구문은 True가 되어, 이 아래의 코드가 실행됩니다.

b. 임포트

# main.py
import script
  • __name__: "script"
  • 출력:
    (아무것도 출력되지 않음)

이 경우 if __name__ == "__main__": 구문은 False가 되므로, 이 조건문 아래의 코드가 실행되지 않습니다.

6. 왜 이렇게 설계되었을까?

이 구조는 파이썬의 모듈화재사용성을 촉진하기 위해 설계되었습니다. 모듈을 독립적으로 테스트하고, 동시에 다른 모듈에서 임포트할 때 불필요한 코드 실행을 방지하려는 목적이 있습니다.

7. 요약

  • __name__ 변수: 모듈의 이름을 저장하며, 파일이 직접 실행될 때는 "__main__"으로 설정됩니다.
  • if __name__ == "__main__": 구문: 파일이 직접 실행될 때만 특정 코드가 실행되도록 하는 조건문입니다.
  • 목적: 모듈을 독립적으로 실행하거나, 임포트된 상태에서 다른 파일에서 재사용할 때 의도치 않은 실행을 방지하고, 모듈화된 코드를 쉽게 테스트할 수 있도록 도와줍니다.

이 설계 덕분에 파이썬 코드가 더 유연하고 재사용 가능하게 되며, 코드의 테스트와 유지보수가 용이해집니다.

profile
AI Engineer / 의료인공지능

0개의 댓글