Python Import module

박상영·2020년 5월 29일
0

Import sys
print(sys.modules)
print(sys.path)

sys를 import 해서 위 print를 해서 직접 보면 큰 차이가 있지만, 우선 먼저 Python 에서 module 이나 package 를 찾는 순서에도 차이가 있다.
sys.modules -> built-in modules -> sys.path

  1. sys.moduels는 단순한 dictionary 형태이고 이미 import 한 package 들을 전부 저장하고 있다. 한번 import 된 module 과 package 들은 Python에서 또 다시 import 하여 불러오지 않아도 되도록 하는 기능을 가지고 있다.
  1. built-in modules 는 파이썬에서 제공하는 파이썬 공식 라이브러들 입니다. Built-in module들은 이미 파이썬에서 포함되어 나오므로 파이썬이 쉽게 찾을수 있다.

  2. sys.path 는 Python 에서 import 를 했을때 가장 마지막으로 찾게 되는 장소다. sys.path 는 기본적으로 list형 이며 string 요소들을 가지고 있는 list 이다. 각 string 요소들의 경로는 이렇게 나온다.

/Users/park/Documents', ' Documents에서 다음 으로 넘어갈때 한번 띄어쓰기가 된것을 볼수있다. 이는 Python 이 path에서 한곳 한곳 일일이 찾아보는것을 의미한다. 하지만 path에서 마저 찾지못하면 ModuleNotFoundError 에러를 리턴한다.

Assignment

1.sys.modules 와 sys.path의 차이점

찾는 순서

파이썬은 import 하고자 하는 module 과 package 를 찾을때 먼저 sys.modules를 보고, 그다음 built-in modules 를 확인 한다음 마지막으로 sys.path 에 지정되어 있는 경로들을 확인해서 찾는다.

sys.path의 경로추가

sys.modules에는 이미 import 되어있는 module 과 package 의 경로가 저장되어 있어 다시 찾지 않아도 된다. 하지만, sys.path는 sys.path에 저장되어있는 경로 하나 하나를 찾아본다. 또한 list형 이므로 경로추가할수있고, 그 추가한 파일명을 import 할수있다.

import sys
sys.path.append("string(경로)")
print(sys.path)
['', '/Users/park/Documents', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python38.zip', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/lib-dynload', '/Users/park/Library/Python/3.8/lib/python/site-packages', '/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages', 'string']

import  string

2.sys 모듈의 위치

sys를 import 한다음 sys.modules를 보면 'sys': <module 'sys' (built-in)> sys모듈 또한 built-in 에 포함되는 것 을 알수있다. 이미 sys는 python자체에 내장된 built-in 모듈 중 하나 인 것이다. 그러므로 built-in modules 에서 찾게 된다.

3.Absolute path(절대경로) 와 relative path(상대경로)의 차이점

Absolute path(절대경로) 는 import 를 하는 파일이나 경로에 상관없이 항상 경로가 동일하다.

import os
os.getcwd() # 현재 자신의 디렉터리 위치 돌려주기
/Users/park/Documents # 현재 os디렉터리의 위치를 보여준다

먼저 os module 을 import 해서 os의 경로를 보겠다. os module 은 환경 변수나 디렉터리, 파일 등의 os자원을 제어할 수 있게 해주는 module이다.

abspath = os.path.abspath("./summer.jpg")
print(abspath)

그다음 경로를 설정해준 값을 abspath에 입력하여 출력까지 해본결과
/Users/park/Documents/summer.jpg
Documents 뒤에 설정해준 jpg가 입력되어 경로가 설정된것을 볼수있다.

Relative path(상대경로) 는 absolute path 와 다르게 프로젝트의 최상단 디렉토리를 기준으로 경로를 잡는게 아니라 import 하는 위치를 기준으로 경로를 정의한다.

위에서 절대경로를 설정한것처럼 상대경로 또한 설정해서 출력까지 해보자면

relpath = os.path.relpath(abspath, "/Users/park")

설정한 abspath값과 최상단 디렉토리 경로를 입력해주었다.
Documents/summer.jpg 위에 절대경로의 값과는 다르게 경로 길이가 줄어든것을 볼수있다.

편하지만 편하지않은 상대경로
불편하지만 불편하지않은 절대경로 라는 느낌을 받았다.

절대경로는 상대경로 보다 경로설정의 길이가 길어 상대경로를 사용하여 길이 자체를 줄일수있다. 하지만, 한번 헷갈리기 시작하면 계속 헷갈리고 파일 위치가 변경되면 그 해당 경로 위치도 변경해주어야 한다.

직접 개발한 Local package

로컬패키지를 import 할때는 해당 package 의 위치에 맞게 import 경로를 선언해야한다. 이 경로 선언에도 위에 작성한 abspath 와 relpath가 쓰인다.

Absolut path를 이용한 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를 가지고있다.
package2 에는 subpackage1 이라는 중첩 package 를 가지고있다.
package1 과 package2를 import 하면
from package1 import module1 ## package1 에서 moudule1을 import
from package1.module2 import function1 from package2 import class1 ## package1의 module2에서 function1 을 import
from package2.subpackage1.module5 import function2 ##package2 의 sub_subpackage1의 module5에서 function2 를 import
절대경로여서 상당히 길게 from 에서 설정해줘야하는것을 볼수있다.
경로들의 시작점이 전부 "my_app" 프로젝트의 가장 최상위 디렉토리에서 시작하는 것을 볼 수 있다.
프로젝트 내에서는 어느 파일, 어느 위치에서 import 를 해도 경로는 항상 동일하게 되므로 절대경로라 하는것이다.

참고 : current directory 라고 하는 현재의 프로젝트 디렉토리는 defualt 로 sys.path 에 포함되게 된다. 그러므로 absolute path 는 current directory 로 부터 경로를 시작하게 된다.

Relative path를 이용한 import

상대경로 를 이용하면 import 하는 위치를 기준으로 경로를 정의한다. 그래서 일반적으로 상대경로는 로컬패키지 안에서 다른 로컬패키지를 import 할때 사용된다.

└── my_app
    ├── main.py
    ├── package1
    │   ├── module1.py
    │   └── module2.py
    └── package2
        ├── __init__.py
        ├── module3.py
        ├── module4.py
        └── subpackage1
            └── module5.py

package2의 module3에서 package2의 class1과 package2의 하위 package 인 subpackage1 의 module5의 function2 함수를 import 하려고 하면

# package2/module3.py

from . import class1
from .subpackage.module5 import function2

dot(.)은 import 가 선안되는 파일의 현재 위치를 말한다. 현재위치는 package2/module3.py 이므로 현재 위치에서부터 원하는 module 의 path만 선언해주면 된다.

dot(.) 2개 사용하기

# subpackage1/module5.py
from ..module4 import class4

(..)은 현재위치에서 상위 디렉토리로 가는 경로입니다.

4.Calculator 패키지

main.py
메인파일에서 rel path 로 add_and_multiply 를 import 를 실행하기.

상대경로로 import 를 실행했을때 ImportError: attempted relative import with no known parent package "알려진 상위 패키지가 없는 상대" 라는 importError 가 발생했다.

참고자료 https://docs.python.org/3/tutorial/modules.html#intra-package-references

참고자료에 따르면 상대경로로 import 하는것은 현재 module 의 이름을 기반으로 한다.
기본 module의 이름 항상 "__main__" 이므로 파이썬의 응용프로그램의 기본 모듈로 사용하려고하는 module은 항상 절대경로 를 설정해주어야한다.

이 내용에 따르면 main.py에서 import 하려고하는 add_and_multiply 는 상대경로가 아닌 절대경로를 사용하여 import 해야한다는 말이다.


위에 상대경로로 되어있는 값을 절대경로로 수정한 뒤 출력할시에
(4 * 3) + (4 + 3) == 19 원하는 출력값을 얻을수있었다.

add_and_multiply.py 에서 import 하기

from calculator.multiplication import multiply
from .multiplication import multiply
절대경로로 설정하여 import 하였을때
"No module name "calculator"("calculator" 라는 이름의 모듈이 존재하지않는다") 라는 Error가 발생했다.
add_and_multiply.py 는 이미 multiplication 과 동일한 package 안에 존재합니다.

그러므로 calculator를 경로지정에서 빼고 from multiplication import multiply muliplication.py 의 multiply 를 import 하면 됩니다.

상대경로를 설정하여 import 하는것은 import 하는 위치를 기준으로 경로를 정의한다. local package 안에서 다른 local package를 import 할때 사용되지만 multiplication은 동일한 local package에 위치하고있으므로 상대경로가 아닌 절대경로를 사용하여 import 하여야 합니다.

__init__.py 파일의 역활
__init__.py 의 역활은 이 파일이 위치한 경로를 package module 처럼 사용할 수 있도록 해줍니다.
만약 calculator 디렉토리 안에 __init__.py 가 존재하지않았다면 안에있는 package들은 사용할수 가 없게됩니다. 그 파일들은 package가 아니게 되기 때문입니다.

위에 main.py 에서 __init__.py 파일을 빼서 import 를 해보면 ModuleNotFoundError: No module named 'multiplication' 모듈을 찾을수없다고 합니다.
하지만, add_and_multiplication.py 에서는 정상작동을 합니다.
이는 이미 같은 디렉토리에 위치하여있기때문에 import 를 할수있지만, 상위 디렉토리인 main.py에서는 package로 받아야 하기때문에 __init__.py 파일이 존재하지 않으면 import 를 할 수가 없습니다.

profile
backend

0개의 댓글