💡 왜 Module을 사용할까?
- 다른 파일에서 재사용이 가능하게 하기 위해
- 전체 코드가 한 파일에 넣기에는 너무 클 때 여러 파일로 나누어서 정리 및 관리하기 위해
## my_module.py my_module_var = 7 def my_module_func(): return "Hello!" class MyModuleClass: pass
module
을 사용할 파일에서import
키워드를 사용하여 불러온 후<module 파일 이름>.<module 안에 작성한 variable, function, class의 이름>
으로 사용Exemple
my_module.my_module_func()
Result
Hello!
from <Module 이름> import <변수/함수/클래스 1>, <변수/함수/클래스 2>...<변수/함수/클래스 n>
from my_module import my_module_func, my_module_var print(my_module_var) my_module_func()
from <Module 이름> import *
from <Module 이름> import <변수/함수/클래스> as <사용자 지정 이름>
import <Module 이름> as <사용자 지정 이름>
💡왜 module에 이름을 붙일까?
1. 서로 다른 모듈에서 동일한 이름의 변수, 함수, 클래스가 있다면 충돌할 수 있고
2. 원하는 요소의 이름이 너무 길수도 있기 때문에
import pkg.mod1 from pkg.mod2 import func2 pkg.mod1.func2() func2()
__init__.py
파일로 Package의 초기 설정을 해줄 수 있다.__init__.py
의 코드들이 자동으로 실행 된다.__init__.py
파일을 사용할까?# __init__.py from .mod1 import func2
# main.py from pkg import func2 # 이미 pkg.mod1에 있는 함수지만 __init__.py에서 이미 경로를 지정했기때문에 # 모듈 파일을 지정하지 않고 사용가능 func2()
__init__.py
파일 내 __all__
변수를 통해 사용할 요소를 지정하여 제한 시킬 수 있다.__all__
변수의 Default 값은 모든 변수,함수,클래스 (때문에 따로 정의를 하여 요소를 제한 시킴)__all__
변수는 String
값의 요소를 갖고 있는 List
(List of String)String
으로 List
에 선언# __init__.py from .mod1 import func2 from .mod2 import func3 __all__ = ['func2', 'func3'] #__all__ 변수에 사용할 함수 정의
# main.py from pkg import * func2() func3() func4() ## <== Error. func4 함수는 __all__ 에 정의되지 않았으므로 import 될 수 없음.
💡왜 import 할 수 있는 요소를 제한할까?
- 내부적으로만 사용되어야 하는 요소가 존재 한다.
- 이러한 요소가 Package 외부에서 Import 되는것을 막기 위해 제한 시킨다.
pip install Django
sys.modules
은 단순한 dictionary
sys.modules
에서 찾을 수 없다.💡sys.modules에 import된 Module, Package를 확인 하는 방법
import sys print(sys.modules)
{ 'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>,... 'enum': <module 'enum' from '/usr/lib/python3.8/enum.py'>, 'signal': <module 'signal' from '/usr/lib/python3.8/signal.py'> }
dictionary
타입임을 확인 할 수 있다.
print()
, set()
과 같은 함수들이 해당된다.List
이며 String
요소들을 갖고 있다.List
의 각 경로를 하나씩 확인해 가면서 해당 경로에 import 한 Package가 위치해 있는지 확인한다.sys.path
에서도 못 찾는다면 Python은 ModuleNotFoundError
에러를 리턴한다.💡sys.path 경로 확인 방법
import sys print(sys.path)
['/home/runner/Python-Training', '/opt/virtualenvs/python3/lib/pyth on3.8/site-packages', '/usr/lib/python38.zip', '/usr/lib/python3.8' , '/usr/lib/python3.8/lib-dynload']
List
타입임을 확인 할 수 있다.
sys.path
에 이미 포함되어 있다.Absolute Path 사용방법
└── my_app ├── main.py ├── package1 │ ├── module1.py │ └── module2.py └── package2 ├── __init__.py ├── module3.py ├── module4.py └── subpackage1 └── module5.py
위와 같이
package1
,package2
이라는 두개의 패키지와package2
와 같이subpackage1
이라는 중첩 package를 갖고 있는 프로젝트에서package1
,package2
를 import 한다고 가정할 때 다음과 같이 import 하면 된다.from package1 import module1 from package1.module2 import function1 from package2 import class1 from package2.subpackage1.module5 import function2
my_app이라는 최상위 directory에서 시작하여 경로를 지정한다. 따라서
subpackage1
의module5
module의function2
함수를 import 하기 위해 다음과 같은 경로를 거치게 된다.
- my_app ➡️ package2 ➡️ subpackage1 ➡️ module5
만약 Python이 리눅스라면 백슬래시(/) 윈도우라면 슬래시() 형태로 이 경로가 나타날 것이고 Python은 이점(.)을 이용하여 이 경로를 표현한다. 다만 최상위 directory인 my_app은 current directory라고 하는 현재의 프로젝트 디렉토리는 Default로
sys.path
에 포함되어 생략되고 다음과 같이 표현 가능하다.
package2.subpackage1.module5
Relative Path 사용 방법
└── my_app ├── main.py ├── package1 │ ├── module1.py │ └── module2.py └── package2 ├── __init__.py ├── module3.py ├── module4.py └── subpackage1 └── module5.py
위와 같이
package1
,package2
이라는 두개의 패키지와package2
와 같이subpackage1
이라는 중첩 package를 갖고 있는 프로젝트에서package2
의module3
에서package2
의class1
과package2
의 하위 package인subpackage1
의module5
의function2
를 import 한다고 가정할 때 다음과 같이 import 하면 된다.# package2/module3.py from .import class1 from .subpackage1.module5 import function2
여기서 점(.)은 import 하는 파일의 현재 위치를 말한다. 즉, 위 예제에서
module3
가 위치해 있는 경로인 package2가 현재 경로, 점(.)인 것이다.
그럼 만약subpackage1
에서package2
의module4
를 import 하려고 한다면 어떻게 해야 할까.# subpackage1/module5.py from ..module4 import class4
그럴 때는 상위 디렉토리로 이동해야 하므로 점(.)을 2개 사용하여(..) 상위 디렉토리로 이동해야 한다.
(터미널에서 폴더 이동하는 것과 같다)
sys.modules
sys.modules
은 단순한 dictionary
sys.modules
에서 찾을 수 없다.sys.path
List
이며 String
요소들을 갖고 있다.List
의 각 경로를 하나씩 확인해 가면서 해당 경로에 import 한 Package가 위치해 있는지 확인한다.sys.path
에서도 못 찾는다면 Python은 ModuleNotFoundError
에러를 리턴한다.위에 잠깐 정리가 되었지만, sys.modules
를 출력해보면 다음과 같은 결과를 얻을 수 있다.
import sys print(sys.modules)
{ 'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>,... 'enum': <module 'enum' from '/usr/lib/python3.8/enum.py'>, 'signal': <module 'signal' from '/usr/lib/python3.8/signal.py'> }
여기서 제일 첫 줄을 보면 'sys': <module 'sys' (built-in)>
임을 확인 할 수 있다. 즉, Python은 sys module을 built-in modules
에서 찾는다는 것을 확인 할 수 있다.
Absolute Path
Relative Path
만약 main 모듈에서 Relative Path로 module를 import하면 다음과 같은 오류가 발생된다.
Attempted relative import beyond top-level package
원인은 Python documents에서 찾을 수 있는데 다음과 같이 안내 하고 있다.
Since the name of the main module is always "main", modules intended for use as the main module of a Python application must always use absolute imports.
Python에서 Relative Path는 항상 Module의 이름을 기준으로 경로를 찾는데 main module의 이름은 항상 main이다. 이를 알 수 있는 방법은 __name__
이란 변수를 호출해 보면 알 수 있다.
💡예제 코드의 각 모듈에
__name__
변수를 출력하도록 수정 한 후 실행 해보자.Code
# main.py # absoulte path #from calculator.add_and_multiply import add_and_multiply # relative path from calculator.add_and_multiply import add_and_multiply if __name__ == '__main__': print(add_and_multiply(1, 2)) print(__name__)
# add_and_multiply.py from .multiplication import multiply # from calculator.multiplication import multiply def add_and_multiply(a, b): print(__name__) return multiply(a, b) + (a+b)
# multiplication.py def multiply(a, b): print(__name__) return(a*b)
Result
calculator.add_and_multiply #add_and_multiply.py의 __name__ calculator.multiplication #multiplication.py의 __name__ 5 #add_and_multiply module에서 리턴된 값 __main__ #main.py의 __name__
따라서 main module에서 Relative Path를 통해 모듈의 경로를 찾을수 없으며 main module에서는 항상 Absolute Path를 사용해야 한다. 따라서 예제 코드를 수정 하면 다음과 같다.
# absoulte path from calculator.add_and_multiply import add_and_multiply # relative path #from .calculator.add_and_multiply import add_and_multiply if __name__ == '__main__': print(add_and_multiply(1,2))
from calculator.multiplication import multiply def add_and_multiply(a, b): return multiply(a, b) + (a+b)
Python이 Module을 찾는 순서는 다음과 같다.
from .multiplication import multiply def add_and_multiply(a, b): return multiply(a, b) + (a+b)
Python이 Module을 찾는 순서는 다음과 같다.
__**init**__.py
의 역할# __init__.py from .mod1 import func2
# main.py from pkg import func2 # 이미 pkg.mod1에 있는 함수지만 __init__.py에서 이미 경로를 지정했기때문에 # 모듈 파일을 지정하지 않고 사용가능 func2()
__init__.py
파일 내 __all__
변수를 통해 사용할 요소를 지정하여 제한 시킬 수 있다.__all__
변수의 Default 값은 모든 변수,함수,클래스 (때문에 따로 정의를 하여 요소를 제한 시킴)__all__
변수는 String
값의 요소를 갖고 있는 List
(List of String)String
으로 List
에 선언# __init__.py from .mod1 import func2 from .mod2 import func3 __all__ = ['func2', 'func3'] #__all__ 변수에 사용할 함수 정의
# main.py from pkg import * func2() func3() func4() ## <== Error. func4 함수는 __all__ 에 정의되지 않았으므로 import 될 수 없음.