파이썬에서는 직접 개발한 local package를 제외한 모듈을 import할 때에는 경로가 크게 문제가 되지 않는다.
직접 개발한 local package를 import할 때에는 해당 package의 위치에 맞게 import경로를 잘 선언해야한다.
import하는 경로는 Absolute path / relative path 두 가지가 있다.
Absolute, 절대적인 위치를 기준으로 하는 경로이다.
프로젝트 최상단에서 시작하여 해당 파일의 위치까지의 모든 경로를 나타낸다.
Relative, 상대적인 위치를 기준으로 하는 경로이다.
import하는 지점을 기준으로 일부의 경로만 나타낸다.
└── my_app
├── main.py
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
Absolute path를 사용해서 package1,2를 import 한다면,
from package1 import module1
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2
# 파이썬에서는 dot(.)를 사용해서 경로를 표현한다
# 1개 (.) import가 선언되는 파일의 현재 위치
# 2개 (..) 상위 디렉토리로 이동하는 경로
처럼 표현이 될 수 있으며,
경로들의 시작점이 전부 "my_app" 프로젝트의 최상단에서 시작하는것을 볼 수 있다.
이 경로는 어느 위치에서 사용하던 변하지 않는다.
참고로 current directory 라고 하는 현재의 프로젝트 디렉토리는
default로 sys.path 에 포함된다.
그러므로 Absolute path는 current directory 로 부터 경로를 시작한다.
일반적으로 local package를 import 할 때 사용하지만, 경로가 길어질 수 있다는 단점이 있다.
이러한 단점을 보완하기위해 Relative path가 사용할 수 있다.
package2의 module3에 있다고 가정하고, Relative path를 사용해서
package2의 class1을 import하면
from . import class1
package2의 하위 package인 subpackage1의 module5의 function2 함수를 import하면
from .subpackage1.module5 import function2
처럼 표현이 될 수 있다.
경로의 길이가 줄어든다는 장점이 있지만, 쉽게 틀릴 수가 있다.
또한 파일의 위치가 변경되면 경로도 변경되어야 한다.
웬만한 경우 Absolute path를 사용하는게 권장 !
.
│─── calculator
│ ├── __init__.py
│ ├── add_and_multiply.py
│ └── multiplication.py
└─── main.py
# .pyc파일은 같은 파일명의 .py파일이 다른 곳에서 import되면 생성된다.
위와같은 구조를 가진 폴더 및 파일이 있다.
# absoulte path
from calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
# 정상작동
main.py에서 add_and_multiply에 있는 함수를 사용하는 코드이다.
어떠한 위치에 있어도 사용이 가능한 절대경로를 사용해서 정상적으로 작동시켰다.
하지만 해당 경로를 상대경로로 변경하게되면 에러가 발생한다.
# relative path
from .calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
# ValueError: Attempted relative import in non-package
에러의 발생 원인은 if문에 있는 조건식 때문이다.
if __name__ == "__main__":
해당 조건식은 namespace라는 개념을 알고 있어야 이해가 가능하다.
간단히 설명하자면, 각 모듈은 자신만의 namespace를 갖게 된다.
보통 namespace의 이름은 모듈의 파일명과 같다.
때문에 동일한 모듈 내에서 동일한 이름의 클래스나 함수를 정의할 수 없으며,
모듈은 각각 독립적이어서 두 모듈에서는 동일한 이름을 갖는 클래스나 함수를 정의할 수 있다.
namespace 참고
import sys
sys.path
['', 'C:\\Python34\\Lib\\idlelib', 'C:\\Windows\\system32\\python34.zip',
'C:\\Python34\\DLLs', 'C:\\Python34\\lib', 'C:\\Python34',
'C:\\Python34\\lib\\site-packages']
-----------------------------------
from sys import path
path
['', 'C:\\Python34\\Lib\\idlelib', 'C:\\Windows\\system32\\python34.zip',
'C:\\Python34\\DLLs', 'C:\\Python34\\lib', 'C:\\Python34',
'C:\\Python34\\lib\\site-packages']
# sys -> 모듈 이름
# path -> sys 모듈의 namespace에 담겨있는 name
위 sys 경로코드는 import의 경우에 namespace가 처리되는 경우이다.
하지만 파이썬 인터프리터가 최초로 파일을 읽어서 실행하는 경우에는 실행 전에 특정한 변수값을 설정한다.
그 중에 하나가
__name__
변수를 __main__
으로 설정하는 것이다.
이미지 속에 있는 각각의 파일에서
print(__name__)
코드를 실행시켜 보면
로 나타난다.
main.py에서 실행시킨 결과값이기 때문에
main.py를 제외한 나머지 파일에서는 파일명으로 지정되었다.
main.py => 프로그램의 시작점 파일에서는 namespace가 __main__
으로 지정되어 실행이 되기 때문에 절대경로 밖에 사용할 수가 없게된다.
#absolut path
from .multiplication import multiply
def add_and_multiply(a,b):
print(__name__)
return multiply(a,b) + (a+b)
#relative path
from calculator.multiplication import multiply
def add_and_multiply(a,b):
print(__name__)
return multiply(a,b) + (a+b)
이와는 다르게, 시작점이 아닌 파일에서는 절대경로와 상대경로 모두 정상적으로 작동하게 된다.