TIL 20. 파이썬 모듈에 관하여(절대경로, 상대경로)

윤현묵·2021년 8월 9일
0

Python

목록 보기
5/7


이번에는 파이썬의 모듈에 관려하여 알아보도록 하겠습니다.
먼저 파이썬에서 자주 사용되는 라이브러리, 패키지, 모듈의 개념은 아래와 같습니다.

  • 라이브러리
    여러 모듈과 패키지를 묶어 라이브러리라고 합니다.
  • 패키지
    특정 기능과 관련된 여러 모듈들을 하나의 상위 폴더에 넣어 놓은 것을 패키지라 하며
    패키지 안에 여러가지 폴더가 더 존재할 수 있습니다.
  • 모듈
    특정 기능들(함수, 변수, 클래스 등)이 구현되어있는 파일을 의미합니다.

sys.modules, built-in modules, sys.path

  • sys란?

    sys는 파이썬에서 제공하는 모듈이며 os모듈안에 있습니다. 파이썬 인터프리터가 제공하는 변수와 함수를 직접 제어할 수 있게 해주는 모듈이고 C언어로 작성되어 있습니다. 또한 sys는 built-in 되어 있으므로 bulit-in modules에서 찾을 수 있습니다.

  • sys.module

    파이썬이 모듈이나 package를 찾기위해 가장 먼저 확인하는 곳입니다.
    sys.modules는 단순한 dictionary이고, 이미 import된 모듈과 package들을 저장하고 있습니다. 즉, 한번 import된 모듈과 package들은 파이썬이 또 다시 찾지 않아도 되도록 하는 기능을 가지고 있습니다. 한 번 이상 사용되었거나 이미 내장 되어 있는 module이나 패키지만 존재하므로 새롭게 import되는 것들은 없다고 생각하시면 됩니다.
    -. 파이썬이 제일먼저 모듈이나 패키지를 찾는곳
    -. Dictionary 구조
    -. import 되있는 모듈과 패키지 저장 (다시 찾지 않아도 됨)

  • built-in modules

    파이썬에서 공식으로 제공하는 라이브러리. 당연히 설치하자 마자 깔리는 것들이고 쉽게 찾을 수 있게 됩니다. 위에 sys.modules print() 출력 결과를 확인하게 되면 어떤 것이 built-in modules인지 쉽게 확인 할 수 있습니다.

  • sys.path

    파이썬이 module이나 package를 찾을 때 가장 마지막으로 확인하는 부분입니다. pip로 새롭게 설치한 패키지도 이 곳을 통해 찾게 되며 새롭게 작성한 패키지나 모듈을 사용하고자 할 때 이 곳에 path를 등록해서 찾을 수 있도록 설정해 줍니다. 해당 변수는 list 형태로 구성되어 있어 쉽게 할당 및 삭제가 가능합니다
    -. 모듈과 패키지를 1,2를 찾고 마지막으로 sys.path를 찾음
    -. 리스트구조(string 요소)
    -. 처음의 리스트 요소부터 마지막까지 찾음
    -. 파이썬에 포함되어있는 built-in modules, sys.path에서도 모듈을 발견하지 못하면    ModuleNotFoundError 에러를 리턴

    1
    2
    3
    4
    5
    6
    7
    8
    import sys
    print(sys.modules)
     
    {'sys'<module 'sys' (built-in)>'builtins'<module 'builtins' (built-in)>'_frozen_importlib'<module '_frozen_importlib' (frozen)>'_imp'<module '_imp' (built-in)>'_thread'<module '_thread' (built-in)>'_warnings'<module '_warnings' (built-in)>'_weakref'<module '_weakref' (built-in)>'_frozen_importlib_external'<module '_frozen_importlib_external' (frozen)>'nt'<module 'nt' (built-in)>'_io'<module 'io' (built-in)>'marshal'<module 'marshal' (built-in)>'winreg'<module 'winreg' (built-in)>'time'<module 'time' (built-in)>'zipimport'<module 'zipimport' (frozen)>'_codecs'<module '_codecs' (built-in)>'codecs'<module 'codecs' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\codecs.py'>'encodings.aliases'<module 'encodings.aliases' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\encodings\\aliases.py'>'encodings'<module 'encodings' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\encodings\\__init__.py'>'encodings.utf_8'<module 'encodings.utf_8' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\encodings\\utf_8.py'>'_codecs_kr'<module '_codecs_kr' (built-in)>'_multibytecodec'<module '_multibytecodec' (built-in)>'encodings.cp949'<module 'encodings.cp949' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\encodings\\cp949.py'>'_signal'<module '_signal' (built-in)>'encodings.latin_1'<module 'encodings.latin_1' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\encodings\\latin_1.py'>'_abc'<module '_abc' (built-in)>'abc'<module 'abc' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\abc.py'>'io'<module 'io' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\io.py'>'__main__'<module '__main__' from 'c:\\Users\\samsung\\OneDrive\\바탕 화면\\python\\practice.py'>'_stat'<module '_stat' (built-in)>'stat'<module 'stat' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\stat.py'>'_collections_abc'<module '_collections_abc' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\_collections_abc.py'>'genericpath'<module 'genericpath' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\genericpath.py'>'ntpath'<module 'ntpath' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\ntpath.py'>'os.path'<module 'ntpath' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\ntpath.py'>'os'
    <module 'os' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\os.py'>'_sitebuiltins'<module '_sitebuiltins' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\_sitebuiltins.py'>'site'<module 'site' from 'C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\site.py'>}
     
    print(sys.path)
    ['c:\\Users\\samsung\\OneDrive\\바탕 화면\\python''C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\python39.zip''C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\DLLs''C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib''C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39''C:\\Users\\samsung\\AppData\\Local\\Programs\\Python\\Python39\\lib\\site-packages']
    cs

파이썬이 sys 모듈의 위치를 찾는 방법

sys는 위의 출력 결과를 보면 아래와 같이 built-in modules 라는 것을 확인할 수 있습니다.
이미 built-in 되어 있기 때문에 built-in module들이 있는 부분에서 찾게 되며, 파이썬 설치시 기본적으로 내장 모듈에 대한 path 정보가 default 값으로 지정되어 있습니다.

'sys': <module 'sys' (built-in)>

Absolute path와 Relative path의 차이점

  • Absolute path
    프로젝트 폴더 최상위(root)기준으로 경로가 표시되며, 모듈 및 패키지의 시작부터 끝까지 생략이나 축약되지 않고 명확히 명시된 경로를 뜻합니다. 최상위 루트부터 경로를 표시해야해서 경로를 길게 표시해야하는 단점이 있지만 어디에서 쓰든 항상 같아서 사용하는데 헷갈리지 않다는 장점이 있습니다.

  • Relative path
    최상위 경로가 아닌 현재 자기가 속한 경로를 기준으로 정의하며, 주로 local package안에서 다른 local package를 참조할 때 쓰입니다. absolute path에 비해 간결해 지는 장점이 있지만 프로젝트가 커지고 복잡도가 높아질수록 혼동이 될 수 있으며 만일 파일의 위치가 바뀌면 그에 맞춰 path도 재설정 해주어야 합니다. 따라서 아무리 복잡한 프로젝트라 하더라도 코드의 일관성 및 실수를 막기 위해 absolute path의 사용이 권장됩니다.

calculator 패키지 만들기


위의 구조와 같이 calculator 패키지를 만들었습니다.

main.py에서 상대경로로 add_and_mutiply를 임포트 했을 때 아래와 같이 Import 에러가 발생하였습니다.

ImportError: attempted relative import with no known parent package

  • 에러 발생 원인
    -. 상대경로를 이용하여 import 하기
    1) __name__변수에 값으로 해당 모듈의 위치를 파악
    2) 만약 __main__으로 설정될 경우(실행하는 주 스크립트 파일로 직접 실행할 경우) 패키지의 구조가 실제 파일 위치에 관계없이 최상위 모듈인 것처럼 된다.(즉, __main__이 최상위 파일인 것처럼)
    3) 그러면 현재 main.py는 add_and_multiply.py가 있는 calculator패키지와 구분되어 실행을 시키면 내가 제일 최상위 모듈라는 의미로 "ImportError: attempted relative import with no known parent package"라는 에러가 발생

  • 에러 해결 방법
    절대경로로 표현하여 최상위 폴더가 calculator라는 것을 알려준다.

    from calculator.add_and_multiply import add_and_multiply
    5 #출력 확인

main module에서는 패키지의 모듈을 어떻게 import 해야할까?

파이썬 인터프리터를 실행하는 __name__이 main인 스크립트에서는 모듈을 import할 때 절대경로를 이용해야하고 직관성과 명확성을 위해 이유가 있는 경우를 제외하고는 절대경로를 이용하는 것이 좋습니다.

  • __name__ 변수란?
    파이썬이 내부적으로 사용하는 특별한 변수 이름입니다. 모듈은 다른 모듈에서 호출되거나, 직접 인터프리터에서 실행이 되는데 인터프리터에서 모듈을 실행하는 경우 __name__ 변수에 __main__ 값이 저장됩니다.
  • '__main__'는 코드가 실행되는 최상위 스코프의 이름으로, 인터프리터에서 모듈을 실행 시, 모듈의 __name__ 변수는 '__main__'으로 설정이 됩니다.
    이 경우 현재 스크립트 파일이 프로그램의 시작점이 맞는지 판단하는 작업으로 스크립트 파일이 메인 프로그램으로 사용될 때와 모듈로 사용될 때를 구분하기 위한 용도로 사용됩니다.
    일반적으로 if __name__ == '__main__':와 같은 조건문을 이용하여 구분합니다.

add_and_multiply.py에서 multiply함수를 절대경로와 상대경로도 각각 임포트 해보고 main 모듈과 차이점을 생각해보고 결과를 출력해 보세요.

  • 상대경로
    위에서와 같은 이유로 add_and_multiply.py가 __main__이 되므로 상대경로를 사용하면 에러가 발생하게 됩니다.

    from .multiplication import multiply
    ImportError: attempted relative import with no known parent package

  • 절대경로
    지금 import하려는 multiply는 같은 디렉토리내에 있으므로 아래와 같이 실행하면 됩니다.

    from multiplication import multiply

  • main 모듈
    파이썬 인터프리터를 실행한 main.py가 main이 되므로 main에서만 절대경로로 경로를 잘 설정해주면 main외의 모듈들은 절대경로, 상대경로 상관없이 올바르게 실행됩니다.

__init__.py, __all__ 파일의 역할

  • __init__.py
    __init__.py 파일은 해당 디렉토리가 패키지의 일부임을 알려주는 역할을 합니다.
    이 파일이 없다면 패키지로 인식되지 않으며, 추가적으로 패키지내 모듈을 하나로 묶어주고, import하지 않을 모듈을 설정할 수 있습니다.
  • __all__
    다음과 같이 *을 이용해서 import할 때엔 해당 디렉토리의 __init__.py 파일안 __all__에 import 가능한 모듈을 따로 정의해주어야합니다.
    __all__ 로 정의하지않으면 인식되지 않기때문에 사용할 수 없습니다.

참고자료:
https://velog.io/@sji7532/
https://avengersrhydon1121.tistory.com/132
https://velog.io/@hamsterhamin/Python-ImportError-attempted-relative-import-with-no-known-parent-package
https://velog.io/@anjaekk/python%EC%A0%88%EB%8C%80%EA%B2%BD%EB%A1%9C%EC%83%81%EB%8C%80%EA%B2%BD%EB%A1%9C-%EC%83%81%EB%8C%80%EA%B2%BD%EB%A1%9C-import-%EC%97%90%EB%9F%AC%EC%9D%B4%EC%9C%A0%EC%99%80-%ED%95%B4%EA%B2%B0

profile
진정성 있는 개발자를 꿈꾼다

0개의 댓글