파이썬에서 import를 실행하면 모듈이나 Package를 찾기 위해 3가지 장소를 순서대로 확인한다.
- sys.modules
- built-in modules
- sys.path
Question: sys 도 import 해야하는 모듈인데, 파이썬은 sys 모듈의 위치를 어떻게 찾을 수 있을까?
👉 sys 모듈은 이미 파이썬에 내장되어있는 모듈중에 하나이다. 즉, built-in 모듈에 존재하고 있기 때문에 sys를 import할 수 있는 것이다.
└── my_app
├── main.py
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
my_app 이라는 프로젝트 이며 package1과 package2 라는 2개의 package를 가지고 있는 파일 경로를 예로 들면,
Absolute path(절대경로)는 말그대로 경로 전체를 일컫는 말이다.
'Subpackage1' 패키지 아래에 'module5' 모듈 안에 있는 'function2'라는 함수를 import 하려면, 아래와 같이 import 할 수 있다.
from package2.subpackage1.module5 import function2
경로 시작점이 최상위 디렉토리인 my_app에서 시작하는것을 알 수 있다.
즉, 경로의 처음부터 끝까지를 의미힌다.
하지면 이런식으로 import 하게되면 경로가 길어지게 된다. 😩
그래서 존재하는 개념이 relative path 이다.
Relative path(상대 경로)는 import 하는 위치를 기준로의 경로를 의미한다.
예를 들어, package2의 module3에서 package2의 class1과 package2의 하위 package인 subpackage1의 module5의 function2 함수를 import 하려고 하면 다음 처럼 할 수 있다.
# package2/module3.py
from . import class1
from .subpackage1.module5 import function2
*참고) Relative path는 선언해야 하는 경로의 길이를 줄여준다는 장점은 있지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있다.
위 도식도와 같이 'calcuator'라는 패키지를 만들고, main.py에서 아래와 같이 상대경로로 add_and_multiply 라는 모듈을 import 해봤는데,
from .calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
>>>ImportError: attempted relative import with no known parent package
ImportError가 발생되었다.
if __name__ == '__main__':
라는 코드가 사용되었는데, 여기서 __name__ 이라는 변수는 파일 이름에 해당되는데 현재 실행되고있는 파일 안에서는 __main__ 이 된다. 그말인 즉슨 '자기 자신인 모듈을 단독 실행할때'라는 뜻이다.
구글링 결과,
python interpreter는 relative import의 module 위치를 정할 때 __name__속성에 의해 결정된다. 직접 python 파일을 실행시킬 때 __name__ == '__main__' 이 되므로 당연히(?) __main__이라는 모듈의 위치를 파이썬 interpreter는 알 수가 없기 때문에 에러가 발생합니다.
무슨 말인가 한참을 고민해본 결과, 실행하고있는 모듈의 name은 main이 되고 import해오는 다른 모듈들은 해당 모듈의 이름이 된다는 뜻.
+__name__ == '__main__'이 되면 실행 주체가 되는 모듈이기 때문에 절대 경로를 적어줘야된다는 뜻이다. 후...
파이썬에서는 모듈을 단독 실행할때는 상대주소는 쓰지 못하고 절대주소를 쓰게끔 되어있다.(사유는 잘 모르겠다 😂)
👉 직접 실행하는 경우에는 절대경로 import를, 그렇지 않은 경우(=패키지 내부 import)의 경우에는 상대경로 import를 해주면 된다.
그러면 위의 코드에서 정상적으로 작동하게 하려면 상대경로를 절대경로로 바꿔주기 위해 .calculator 앞에 있는 dot . 을 제외시켜주면 된다. 이런 일이 벌어지기 때문에 메인 모듈에서는 다른 모듈을 import 해올때 절대경로로 적어줘야된다. 하지만 import 되는 모듈들은 직접 실행되는 모듈들이 아니기 때문에 상대경로를 적어줘도된다.
#add_and_mulitply.py
1) from .multiplication import multiply
2) from calculator.multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
위의 코드는 add_and_multiply 모듈에 있는 코드이다. 이 모듈의 경우 직접 실행하지 않고 main 모듈에서 import되는 모듈이기 때문에 상대경로로 import가 가능하고 절대 경로로 적어줘도 무방하다.
__init__.py 파일은 해당 디렉터리가 패키지의 일부임을 알려주는 역할도 하고 이름 그대로 패키지를 초기화하는 역할도 한다.
calpkg 패키지 안에 geometry와 operation 모듈이 있고,
main 모듈에서 calpkg 패키지 형식으로만 import 해서 모듈을 불러오고 싶다면
init 파일안에 아래와같이 코드를 작성하면 된다.
#(main.py도 calpkg 안에 속해있다.)
#__init__.py
from . import operation
from . import geometry
#main.py
import calcpkg # calcpkg 패키지만 가져옴
print(calcpkg.operation.add(10, 20)) # operation 모듈의 add 함수 사용
print(calcpkg.geometry.triangle_area(30, 40)) # geometry 모듈의 triangle_area 함수 사용
만약 calpkg.operation.add 말고 add 처럼 함수이름만을 그대로 사용하고 싶다면!
# 현재 패키지의 operation, geometry 모듈에서 각 함수를 가져옴
# 현재 패키지(calcpkg)라는 것을 나타내기 위해 모듈 앞에 .을 붙인다.
#__init__.py
from .operation import add, mul
from .geometry import triangle_area, rectangle_area
혹은 모든 함수를 가져오고 싶으면 아래와 같이 쓰면된다.
from .operation import *
from .geometry import *
만약 *를 특정 함수나 변수,클래스 로로 제한하고 싶으면 아래와 같이 __all__ 안에 넣고 싶은 함수,변수,클래스를 넣으면 된다
#__init__.py
# calcpkg 패키지에서 add, triangle_area 함수만 공개
__all__ = ['add', 'triangle_area']
from .operation import *
from .geometry import *
그러면 메인 모듈에는 아래와 같이 importgodhaus ehlsek
from calcpkg import * # calcpkg 패키지의 모든 변수, 함수, 클래스를 가져옴
print(add(10, 20)) # add 함수는 공개되어 있으므로 사용할 수 있음
print(mul(10, 20)) # 에러: mul 함수는 공개되어 있지 않으므로 사용할 수 없음
print(triangle_area(30, 40)) # triangle_area 함수는 공개되어 있으므로 사용할 수 있음
print(rectangle_area(30, 40)) # 에러: rectangle_area 함수는 공개되어 있으므로 사용할 수 있음