[Python] import path / absolute path, relative path / __name__, __main__

지기성·2022년 4월 3일
0
post-thumbnail

absolute path

이름 그대로 완전한 형태의 경로를 말합니다

relative path

import 하는 위치를 기준으로 상대적인 경로를 말합니다

absolute path와 relative path는 어떤 경우에 사용해야 할까요?

Absolute imports are recommended, as they are usually more readable and tend to be better behaved (or at least give better error messages) if the import system is incorrectly configured (such as when a directory inside a package ends up on sys.path)

However, explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose

https://peps.python.org/pep-0008/#imports

공식문서(PEP8)에 따르면

  1. 절대 경로(absolute path)를 사용할 것을 권장합니다

  2. 다만, 같은 package 속에서 불필요하게 장황한 절대 경로라면 상대 경로(relative path)를 사용할 수 있다

고 쓰여있습니다


PEP (Python Enhancement Proposal) 링크

PEP 8 링크


예시를 통해 알아보겠습니다

my_app 이라는 project를 생성하고 여러 directory와 file을 만들었습니다

absolute path

위 구조에서

main.py 에 module1.py 와 module5.py 를 절대 경로를 통해 import 해보겠습니다

# main.py

from package1 import module1
from package2.subpackage import module5

절대 경로로 import 시 my_app 은 생략된 것을 볼 수 있습니다. my_app이 시작점(entry point) 이기 때문입니다.

절대 경로로 import 시 시작점은 project directory 입니다

my_app 이라는 project를 생성 시 자동으로 sys.path에 요소에 추가되었기 때문에 my_app directory가 시작점으로 설정되는 것 입니다


project directory생성 시 자동으로 sys.path에 요소로 추가됩니다.
import search flow 링크


relative path

상대 경로를 설명하기 위해 my_app directory에 new_module.py를 추가했습니다

module4에서
현재 directoty의 module4.py 와
하위 directory인 subpackage의 module5.py와
상위 directory의 new_module.py 를 import해보겠습니다

# module3.py

from . import module4
from .subpackage import module5
from .. import new_module

import 하는 file의 directory를 시작점으로 잡습니다.

따라서 package2를 시작점으로써, .(Dot)을 통하여 directory를 이동합니다
Dot 갯수별 directory 위치

  • 1개: 시작점 directoy
  • 2개: 상위 directory

하지만 PEP 8에 나와있듯, 상대 경로로 import하는 것은 지양해야합니다.

특히나 상대 경로로 import 했을때 생기는 문제점은
file 위치가 변경된다면 경로도 다시 지정해야 하기 때문입니다!


__init__.py file의 역할

구조를 보면 __init__.py file이 보입니다.

__init__.py 은 해당 directory가 package 임을 알려주는 역할을 합니다.

초기화

class 선언 시 __init__는 magic method로써 사용되며, instance의 초기화를 진행해 주는 역할을 수행합니다

마찬가지로 __init__.py 은 directory 수준(level)에서도 package의 초기화 역할을 수행합니다

package가 import 될 때 초기값 설정

import 할때 경로의 총 길이 줄여줄 수 있고
package에서 import 할 수 있는 변수, 함수, 클래스를 제한할 수 있습니다

Python3.3 버전부터는 __init__.py file이 없어도 패키지로 인식하지만 하위 버전 호환을 위해 __init__.py file을 생성하는 것이 안전한 방법입니다.


실습 (calculator)

직접 실행(run) 하는 module 에 적히는 import 경로는 제약사항이 존재합니다

제약사항을 알아보기위해 실습을 해보겠습니다.

보통 main.py 에서 직접 실행하기 때문에
main.py 에서 import 를 절대 경로로 했을때와 상대경로로 했을때
어떤 일이 발생하는지 알아보겠습니다.

main.py 의 절대경로

C:\Users\jigiseong\PycharmProjects\velog\venv\Scripts\python.exe C:/Users/jigiseong/PycharmProjects/velog/main.py
5

Process finished with exit code 0

main.py 에서 절대 경로로 import 하고 직접 실행하면 정상적으로 실행됩니다
위치를 모두 때려 박으니 당연히 정상작동 하겠죠?

main.py 의 상대경로 ?

아래의 도식처럼 . velog 라는 directory에서

C:\Users\jigiseong\PycharmProjects\velog\venv\Scripts\python.exe C:/Users/jigiseong/PycharmProjects/velog/main.py
Traceback (most recent call last):
  File "C:\Users\jigiseong\PycharmProjects\velog\main.py", line 5, in <module>
    from .calculator.add_and_multiply import add_and_multiply
ImportError: attempted relative import with no known parent package

Process finished with exit code 1

ImportError: attempted relative import with no known parent package

main.py 에서 상대 경로로 import 하고 실행하면
알려진 상위 패키지가 없는 상대 가져오기 시도라는 importError 가 발생합니다.
(즉 entry point가 velog가 아닌 것 입니다)

"main.py는 velog 패키지 맞는데;; 외않되;;;"

상대 경로에서 시작점 directory를 velog로 인식하지 못하는 이유는 직접 실행하는 module은, 이름이 "__main__" 이 되기 때문입니다

공식문서에 따르면

Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.
https://docs.python.org/3/tutorial/modules.html#intra-package-references

  • 결론만 축약해보자면

"직접 실행할 module에서는 항상 절대 경로로 import 해야한다" 입니다

이유를 알기 위해선

  1. Python 에는 module라는 Data model 에는 미리 정의된 속성:__name__ 이 존재하는 것과
  1. 상대경로가 어떻게 기준위치를 계산하는지 알아야 합니다

module은 미리 정의된 속성:__name__ 을 갖습니다
https://docs.python.org/3/reference/datamodel.html#special-method-names
The __name__ attribute must be set to the fully-qualified name of the module.
This name is used to uniquely identify the module in the import system.
https://docs.python.org/3/reference/import.html#import-related-module-attributes

위에서 언급한 이유를 순번에 맞춰 서술해 봤습니다.

  1. module은 미리 정의된 속성으로 __name__ 속성이 존재합니다. __name__ 속성에 에 module 의 이름을 저장합니다.
    근데 직접 실행하는 module은, 이름이 __main__ 이 저장 됩니다. 즉 __name__ = "__main__" 이 할당됩니다

  2. 상대 경로는 해당 속성을 통해 (__name__ 변수에 할당된 문자열을 통해) 위치를 파악하여 경로를 계산합니다

즉, 직접 실행되는 module은 원래의 위치가 아닌 "__main__" 에 해당하는 위치로 계산되기 때문에 상대 경로를 쓸 수 없는 것 입니다!

"위치를 바꿔놓고 예전 위치를 기준으로 경로를 써두니 될리가 없겠죠?"

jiggyjiggy : "a야, 우측으로 1칸, 아래로 1칸 이동하면 누가있지?"
a:  		 "f가 있지"

jiggyjiggy : "a야, 우측으로 1칸, 아래로 1칸 이동하면 누가있지?"
a: 		 	 "아무도 없지;;"

직접 실행되지 않는 module에서의 import 경로

직접 실행되지 않는 module에서의 import 경로는 절대경로, 상대경로 상관 없습니다.
직접 실행되지 않았기 때문에 __name__ 속성이 변경되지 않기 때문이겠죠? ㅎㅎ

하지만 PEP 8에서 언급했듯,
상대경로는 file의 위치가 변한다면 바뀌는 경로이기 때문에 절대 경로로 쓰는 것이 좋은 습관입니다


번외.

Python code를 보다보면

if __name__ == '__main__':

를 자주 접하셨을 겁니다
import 했는데 해당 파일에 함수 호출이 있다면 그대로 실행됩니다
링크로 빼 링크

profile
궁금증 주도 공부 / 원리 파고들기 / 경험에 기반한 블로그

0개의 댓글