main.py 파일에서 모든 변수, 함수, 클래스를 선언해서 사용하면 파일이 너무 커집니다. 또한, 다른 파일에서 main.py에서 선언한 변수, 함수, 클래스를 사용해야 할일도 있습니다.

이럴때 사용하는 개념이 Module, Package 개념입니다.
Module이란 변수, 함수, 클래스를 선언한 파일이고
Package는 이런 Module을 모아놓은 디렉토리라고 생각하면 됩니다.

Module, Package를 사용하면 다른 파일에서 선언한 변수, 함수, 클래스 사용이 가능하고 여러 파일로 나누어 정리가 가능합니다.

즉, 재사용과 정리를 용이하게 하기 위한 개념입니다.


1. Module

1-1. import

다른 파일에서 변수, 함수, 클래스를 사용하기 위해서는 module을 import해야합니다.

import <모듈 이름>

## main.py
import my_module

print(my_module.my_module_var)

my_module.my_module_func()

my_module_class = my_module.MyModuleClass()

여기서는 .(dot notation)을 사용해 변수, 함수, 클래스에 접근합니다.
이는 이름이 매우 길어지고 귀찮습니다.
이를 해결하기 위해 다음 경우를 살펴보겠습니다.

1-1-1. from <모듈> import <변수, 함수, 클래스>

from <모듈 이름> import <함수/변수/클래스1>, ..., <함수/변수/클래스N>
from import 키워드를 사용하여 모듈을 불러들이는 경우
모듈 이름을 붙이지 않고 곧바로 원하는 함수나 변수 그리고 클래스를 호출할 수 있습니다. 모듈에서 사용하는 것이 명확할때 from import 를 사용하면 편리합니다.

from my_module import my_module_func, my_module_var

print(my_module_var)
my_module_func()

이런 식으로 어느 모듈에서 부르는지 확실한 경우에는 .(dot notation)을 사용하지 않아도 됩니다.


그리고 해당 모듈에서 선언된 모든 변수, 함수, 클래스를 import할 때는 *를 사용합니다.

from my_module import *

print(my_module_var)
my_module_func()

하지만 이렇게 하는 것은 권장되지 않습니다. 왜냐하면 local scope를 가지고 있는 다른 변수/함수/클래스 들과 이름 충돌이 날 수 있는데, 만일 이름 충돌이 일어났을 경우 알기가 쉽지 않을수 있기 때문입니다.

1-1-2. import as

from <모듈 이름> import <변수/함수/클래스> as <새로 선언하는 이름>

만일 여러 모듈을 import 하게 되면 이름 충돌이 날 수도 있습니다. 예를 들어 서로 다른 모듈에서 동일한 이름의 함수가 있는 경우입니다. 혹은 원하는 모듈의 요소의 이름이 너무 길수도 있습니다. 이러한 경우 import as 키워드를 사용해서 새로운 이름을 주어서 사용할 수 있습니다.

from my_module  import my_func as f1
from my_module2 import my_func as f2
from my_module3 import function_with_name_too_long as f3

f1()
f2()
f3()

module 이름도 as 를 사용하여 새로운 이름을 줄 수 있습니다.
import <모듈 이름> as <새로운 모듈 이름>

import my_module as m1

m1.my_module_func()

2. Package

Package는 module들을 모아놓은 디렉토리 개념입니다.

스크린샷, 2019-10-13 14-30-57.png
위의 사진과 같은 Package가 있다고 가정해보겠습니다.
이때, mod1과 mod2의 func2를 import하려면 다음과 같이 해주면 됩니다.

import pkg.mod1
from pkg.mod2 import func2

pkg.mod1.func2() # 그냥 mod1을 import한 경우
func2()          # from import를 사용한 경우

2-1. Package Initialization

Python은 __init__.py 파일을 통해 package 초기 설정을 가능하게 해줍니다.

스크린샷, 2019-10-13 14-46-41.png
아까 사진과 다르게 __init__.py파일이 pkg 디렉토리 안에 있습니다.
Package 안에 __init__.py 파일이 있으면 package가 import 될때 __init__.py 파일의 코드들이 자동으로 실행됩니다.

__init__.py파일로 할 수 있는 일들에 대해 알아보겠습니다.

  1. Import 할때 경로의 총 길이 줄여주기
  2. Package에서 import 할 수 있는 변수/함수/클래스 제한하기
  3. 그 외 package가 import될때 꼭 먼저 실행되어야 하는 코드들

2-1-1. import 경로 줄이기

pkg에서 mod1의 func2 라는 함수를 import 하여 사용하기 위해서는 다음과 같이 해야합니다.

#main.py
import pkg.mod1

pkg.mod1.func2()

매번 모든 경로를 타입하지 않는 방법은
__init__.py파일에서 먼저 import하는 것입니다.

# __init__.py
from mod1 import func2
# main.py
from pkg import func2

func2()

2-1-1. import 할 수 있는 변수/함수/클래스 제한하기

__init__.py 파일을 사용해서 import 할 수 있는 변수/함수/클래스를 제한할 수 있습니다.
예를 들어 내부적으로만 사용되어야 하는 함수도 있을수 있는데 이러한 함수가 package 외부에서 import되어 사용되는 것을 막기 위해서는 __all__ 변수를 지정해주면 됩니다.

__all__ 변수의 default 값은 모든 함수/변수/클래스 입니다.
그러므로 __all__ 변수를 따로 정의해줌으로 import 될 수 있는 요소들을 제한할 수 있는 것입니다.
__all__ 변수는 string 값의 요소를 가지고 있는 list 입니다.
그러므로 import 되길 원하는 요소들을 string으로 list에 선언해주면 됩니다.

# __init__.py
from mod1 import func2
from mod2 import func3

__all__ = ['func2', 'func3']
# main.py
from pkg import *

func2()
func3()
func4() ## <== Error. func4 함수는 __all__ 에 정의되지 않았으므로 import 될 수 없음.