만약 import abc 라는 구문을 치고 abc 모듈을 불러오려고 하면 파이썬은 1) sys.modules -> 2) built-in modules -> 3) sys.path 순서로 해당 모듈의 위치를 확인하고 불러온다. sys.path에서도 찾을 수 없으면 에러가 발생한다.
sys.modules는 파이썬이 module이나 pkg를 import 하기 위해 가장 먼저 해당 모듈이 존재하는지 확인하는 곳이다. 한번이라도 import 된 module이나 pkg는 아래와 같이 sys.modules에 딕셔너리 형식으로 저장되므로, 파이썬이 별도의 위치를 찾지 않고 바로 sys.modules에서 불러 올 수 있다.
>>> import sys
>>> print(sys.modlues)
{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>, '_imp': <module '_imp' (built-in)>, '_thread': <module '_thread' (built-in)>, '_warnings': <module '_warnings' (built-in)>, '_weakref': <module '_weakref' (built-in)>, 'zipimport': <module 'zipimport' (built-in)>, '_frozen_importlib_external': <module 'importlib._bootstrap_external' (frozen)>, '_io': <module 'io' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'posix': <module 'posix' (built-in)>, 'encodings': <module 'encodings' from .... '/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/rlcompleter.py'>, 'mod1': <module 'mod1' from '/Users/YB/Google 드라이브/TIL/Documents/Python/mod1.py'>}
그러나 새롭게 import 해야되는 module/pkg는 sys.modules에 존재하지 않으므로 파이썬이 찾을 수 없다.
파이썬에 내장된 모듈들로, sys.modules에서 import 할 모듈을 찾지 못했으면 built-in modules 중에서 확인해보게 된다.
built-in modules에서도 확인할 수 없는 module/pkg를 찾아보는 장소가 sys.path이다. 기본적으로 파이썬이 설치된 디렉토리가 저장되어 있으며, 해당 디렉토리 내에 모듈이 있는지 파이썬이 확인하게 된다.
>>> import sys
>>> print(sys.path)
['', 'C:\\Users\\owner\\AppData\\Local\\Programs\\Python\\Python38-32\\Lib\\idlelib', 'C:\\Users\\owner\\AppData\\Local\\Programs\\Python\\Python38-32\\python38.zip', 'C:\\Users\\owner\\AppData\\Local\\Programs\\Python\\Python38-32\\DLLs', 'C:\\Users\\owner\\AppData\\Local\\Programs\\Python\\Python38-32\\lib', 'C:\\Users\\owner\\AppData\\Local\\Programs\\Python\\Python38-32', 'C:\\Users\\owner\\AppData\\Local\\Programs\\Python\\Python38-32\\lib\\site-packages']
sys.path는 list 형식으로 저장되어 있으며, 새로운 디렉토리를 추가할 수도 있다.
sys.modules와 sys.path 또한 import sys를 입력해서 import 해야된다는 점에서 동일하나, 아래와 같은 차이점을 가진다.
1) sys.modules는 딕셔너리 형태이지만 sys.path는 list 형태로 되어있다.
2) sys.modules는 한번이라도 import 한 module or pkg만 불러올 수 있지만 sys.path는 새로운 module or pkg라도 sys.path에 등록된 디렉토리 내에 있으면 불러올 수 있다.
위에서 print(sys.modules)를 입력했을때 아래와 같이 맨 처음으로 sys가 등장하며, value 값 중 built-in이라고 표시된 부분이 보일 것이다.
>>> import sys
>>> print(sys.modlues)
{'sys': <module 'sys' (built-in)>
이처럼 sys 자체가 파이썬에 내장된 built-in 모듈이므로 파이썬이 쉽게 built-in 모듈 중에서 sys를 가져올 수 있다.
위의 세가지 경로 말고 직접 import할 module or pkg의 경로를 입력해서 import 할 수 있는 방법이 두가지가 있다. 아래와 같이 절대 경로(Absolute path)를 입력하거나 상대 경로(Relative path)를 입력함으로써 sys.path에 위치하지 않은 직접 개발한 local module/pkg를 불러올 수 있다.
import할 module/pkg의 위치를 축약하지 않고 최상위 경로를 시작점으로 해당 module/pkg를 적어준 것이 Absolute path이다. 아래 예를 살펴보자.
예시)
my_app
├── main.py
├── pkg1
│ ├── init.py
│ ├── module1.py
│ └── module2.py
├── pkg2
│ ├── init.py
│ ├── module3.py
│ ├── module4.py
│ ├── module5.py
│ └── pkg4
│ ├── init.py
│ └── module6.py
└── pkg3
├── init.py
└── module7.py
main.py에서 module6.py에 있는 func3이라는 함수를 불러오려면 아래와 같이 최상위 경로인 my_app에서부터의 경로를 적어주어야 된다.
# main.py
from pkg2.pkg4.module6 import func3
이와 같이 Absolute path를 사용하면 module6.py의 위치가 바뀌지 않는 이상 정확하게 해당 module을 불러올 수 있는 장점이 있다.
단, 경로가 복잡할 경우 입력해야 되는 경로값이 길어지는 단점이 존재한다.
Absolute path를 입력하기에는 경로가 너무 길 경우 Relative path를 활용할 수 있다. Relative path는 현재 모듈의 위치를 기준으로 경로를 표현한다.
#module3.py
from .module5.py import func7
같은 폴더에 있는 다른 파일에서 모듈 및 함수 등을 호출하려면 파일명 앞에 .을 붙인다. 한번 사용된 .은 import가 선언되는 파일의 현재위치를 뜻한다.
만약 현재 모듈의 상위 폴더로 나가서 다른 파일을 불러오려고 하면 아래와 같이 ..를 파일명 앞에 붙이고, 상위폴더에서부터의 위치를 작성하면 된다.
#module3.py
from ..pkg3.module7.py import func14
Relative path는 위와 같이 간결하게 표현할 수 있는 장점이 있지만 경로가 복잡해질 경우 실수가 발생할 여지가 많으며, 파일의 위치가 바뀌면 다시 작성해야 된다는 단점이 있다. 따라서 되도록이면 Absolute path를 사용하고 Relative path는 꼭 필요하고 단순하며 변경 여지가 적은 부분에서만 사용하는 것이 추천된다.
실제 Pkg를 만들어보자.
Practice(폴더)
calculator(폴더)
__init__.py
add_and_multiply.py
multiplication.py
main.py
위와 같은 경로를 활용해서 아래와 같이 각각 파일을 만들어 저장해보자.
1) 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))
2) add_and_multiply.py
from .multiplication import multiply
# from calculator.multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
3) multiplication.py
def multiply(a,b):
return(a*b)
4) __init__.py
빈 파일로 저장한다.
__main__
에서 relative path 사용 가능한가?위에서 만든 파일들 중 main.py를 실행시켜보자.
아래와 같이 에러가 뜨면서 동작하지 않는 것을 확인할 수 있다.
importError: attempted relative import with no known parent package
이는 main.py가 최상위 파일이라서 relative path를 사용할 수 없기 때문에 뜨는 에러이다. 에러 문구에도 no known parent package라는 내용이 들어있다.
모듈의 이름을 보여주는 __name__
을 활용한다면 main.py는 __main__
값을 보여줄 것이다. 각 모듈들이 import 되서 작동해야 되는 메인 파일이기에 __main__
값이 뜨는 것이다. 반면 multiplication.py는 __name__
을 사용하면 상위에 존재하는 파일이 있으므로 파일명인 multiplication가 뜨게 될 것이다. 이처럼 __main__
값을 가지는 파일에서는 relative path를 사용할 수 없다.
아래와 같이 relative path가 아닌 absolute path를 사용하도록 코드를 수정하면 파일은 정상적으로 동작하게 된다.
main.py
# absoulte path
from calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
위와 같이 파일을 수정한 상태에서 caculator 폴더 안으로 이동한 후 add_and_multiply.py를 실행해보자.
main.py에서 relative path를 사용했을때와 동일한 에러가 발생한다.
add_and_multiply.py 파일 기준에서는 해당 파일의 위치가 최상위 폴더에 위치하기 때문에 __name__
속성이 __main__
이어서 relative path가 동작하지 않는 것이다.
그렇다면 main.py에서 수정했던 것처럼 absolute path를 사용하면 에러가 해결되는지 시도해보자. 코드를 아래와 같이 수정해본다.
add_and_multiply.py
#from .multiplication import multiply
from calculator.multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
위와 같이 또다시 에러가 등장했다.
이미 해당 파일이 calculator 폴더 안에 있는 파일을 실행하는 것이므로, 아래와 같이 해당 폴더 값을 지워줘야만 에러가 발생하지 않는다.
#from .multiplication import multiply
from multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
main.py에서 add_and multiply를 불러오고, 또 여기서 multiplication을 불러올 때에는 main.py에서 해당 파일들까지 갈 수 있는 모든 경로가 필요하지만, add_and multiply를 직접 실행시에는 main에서 add_and multiply까지의 경로는 필요하지 않기 때문에 main.py에서 정상 작동했더라고 위처럼 에러가 발생할 수 있다.
__init__.py
의 역할__init__.py
는 해당 디렉터리가 패키지의 일부임을 알려주는 역할을 한다. 일반 폴더와 얼핏 보면 구분이 안되게 생긴 파이썬 패키지를 파이썬이 패키지라고 인식할 수 있게 해주는 중요한 파일이다. 해당 파일이 없으면 파이썬은 해당 디렉토리를 패키지로 인식하지 않는다.
__init__.py
파일은 위의 예제처럼 비어있을 수도 있고, 패키지 내에 포함된 모듈들의 정보를 제공하기도 한다.
어떤 패키지에 있는 모든 모듈이나 어떤 모듈에 있는 모든 함수를 불러올때 import *
를 사용한다. 아래 예시를 살펴보자.
main.py
from calculator import *
if __name__ == '__main__':
print(add_and_multiply(1,2))
이렇게 main.py를 작성하고 실행시키면 아래와 같은 에러가 뜬다.
import *
를 통해서 어떤 모듈, 함수를 가져와야 될지 인식을 못해서 발생하는 에러이다. 따라서 이런 경우에는 아래와 같이 __init__.py
에 * 값이 어떤 모듈, 함수를 뜻하는지를 __all__
을 활용해 정의해줘야 정상적으로 작동한다.
__init__.py
from .add_and_multiply import add_and_multiply
__all__=['add_and_multiply']
단, 이렇게 __all__
값을 이용해 특정 모듈, 함수를 정의한 경우 import *
를 했더라도 다른 모듈, 함수값은 import가 되지 않았으므로 직접 import 뒤에 모듈명/함수명을 적어서 불러와야 된다.
참고로 파이썬 3.3 버전 부터는 __init__.py
가 없어도 패키지로 인식이 가능하다고는 하나, 하위 버전 호환을 위해 __init__.py
를 사용하는 것이 권장된다.