1월 29, 2022

Calculator 패키지 만들기

 이러한 구조의 패키지를 만들고 작동시켜보는것이 과제였다



  • ImportError: attempted relative import with no known parent package

→ main.py에서 상대경로로 add_and_multiply 임포트시 발생하는 에러

1
2
3
4
5
#relative path
from .calculator.add_and_multiply import add_and_multiply

if __name__ == '__main__': 
		print(add_and_multiply(1,2))

왜??

main 모듈에서 상대경로를 사용할 경우 파이썬이 상대경로의 출발점, 즉 main 모듈의 위치를 찾지 못하기 때문이다.

모듈을 사용할 때에는 2가지 방법이 있다

  1. 인터프리터에서 직접 실행하거나
    1. 이때 모듈의 이름은 자동적으로 main 으로 변경
  2. 다른 모듈에서 import해 실행하거나

모듈실행 뒤 상대경로를 통해 다른 모듈을 import 할 때,

파이썬은 모듈의 이름(name)에 기반을 두고 현재모듈의 위치를 찾는다

따라서 이름이 main 으로 바껴버린 모듈은 파이썬이 위치를 찾을수가 없음

relative path의 출발점이 되는(현재 import하는 위치인 모듈, 즉 __main__으로 이름이 바뀐 모듈) 위치를 못 찾아 위와 같은 에러가 발생하는 것

  • main module에서는 패키지의 모듈을 어떻게 임포트 해야 하는가??

(참조 : https://docs.python.org/3/tutorial/modules.html#intra-package-references)

따라서 main module에서는 절대 경로를 사용해주어야 한다.

1
2
3
4
5
#absolute path
from calculator.add_and_multiply import add_and_multiply

if name == 'main':
print(add_and_multiply(1,2))

“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.”

“relative import(상대가져오기)는 현재 모듈의 이름에 기반해있음을 기억하라. main module의 이름이 항상 main 이기에, 파이썬의 main module로 쓰여지기 위한모듈들은 항상 absolute import(절대가져오기)를 사용해야 한다”

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




     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    #from .multiplication import multiply
    from calculator.multiplication import multiply
    
    def add_and_multiply(a,b):
        return multiply(a,b) + (a+b)
    
    >> No module named 'calculator'
    
    절대 경로 실행시
    
    from .multiplication import multiply
    #from calculator.multiplication import multiply
    
    def add_and_multiply(a,b):
        return multiply(a,b) + (a+b)
    >>attempted relative import with no known parent package
    상대 경로 실행시
    

    디렉토리 구조상 두개다 문제 없이 실행되는것이 옳아보임에도 문제가 난다 흐음..

    절대경로 실행시에는 calculator패키지가 존재하지 않는것처럼 결과가 출력됐다.

    상대경로실행시에는 main.py에서 상대경로로 import할때와 같은 에러가 나온다

    에러 구글링도 해보고 여러 삽질을 하다가.. vscode를 끄고 폴더에 직접 들어가서 문제점을 찾을 수 있었다... 위의 주소에 에러의 원인이 있었다..



    (main.py는 calcuator 패키지 안에 있다)

    아니다..헛짚었다.. 파일 구조를 수정해줬음에도 여전히 같은 오류가 난다

    각고의 구글링을 통해 나와 같은 문제를 겪으신것으로 추정되는 선배님(??)의 블로그에서 해결방안을 찾았다.

    https://velog.io/@hkja0111/TIL-15-Import-Module-Package - 감사합니다..

    add_and_multiply가 현재 마치 main.py 처럼 메인 모듈 행세를 하고 있는 듯 하여(add_and_multiply가 current directory로 작동하기에, absolute path를 사용하려면 current directory인 add_and_multiply부터 시작하는것이 맞으니 caculator로 올라가지 않고 바로 multiplication부터 시작하면 되지 않나 하여) main.py의 경우처럼 절대경로로 import해보았다

    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # from .multiplication import multiply
    from multiplication import multiply
    
    def add_and_multiply(a,b):
        return multiply(a,b) + (a+b)
    
    print(add_and_multiply(1,2))
    
    >>5
    

    정상작동한다.. add_and_multiply.py 에서 실행한 경우 다른 모듈로서 import돼 실행된 것이 아니라 직접 실행(인터프리터상에서 직접실행하고 있다) 했기에 add_and_multiply.py가 메인 모듈의 취급을 받은 것이다. 그렇기 때문에 상위의 calculator 패키지가 존재하지 않는 것처럼 에러가 나왔고, 상대경로 또한 사용할 수 없었던 것이다





    위에 있던 이 글을 기억하자....

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from .multiplication import multiply
    #from multiplication import multiply
    
    def add_and_multiply(a,b):
        return multiply(a,b) + (a+b)
    
    print(add_and_multiply(1,2))
    
    >> ImportError: attempted relative import with no known parent packa
    
    
    

    ⇒ 메인 모듈의 역할을 하기에 상대경로는 사용할 수 없는 모습이다


  • __init__.py 파일의 역할 정리하기

__init__.py 는 자신이 위치한 디렉토리가 패키지의 일부임을 확인해주는 역할을 한다.

python 3.3버전부터는 __init__.py가 없어도 패키지로 인식하지만, 하위호환을 위해 생성하는 것이 안전하다.

a. Import 할때 경로의 총 길이 줄여주기

    



b. Package에서 import 할 수 있는 변수/함수/클래스 제한하기

내부적으로만 사용돼야 하는 함수가 package외부에서 import되어 사용되는 것을 막기 위해서 __all__ 변수를 지정해 줄 수 있음

package를 통해 import 될 수 있는 요소들은 모두 __all__ 변수를 통해서 정의

__all__ 변수의 default 값은 모든 함수/변수/클래스임

그러므로 __all__ 변수를 따로 정의해줌으로 import가 될 수 있는 요소들을 제한 할 수 있는

__all__ 변수는 string 값의 요소를 가지고 있는 list(list of strings)

그러므로 import 되길 원하는 요소들을 string으로 list에 선언해주면 됨