ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] 모듈 상대경로(ImportError: attempted relative import with no known parent package)
    💫 Computer Science/Python & AI Framework 2021. 10. 8. 11:29

     

     

    파이썬에서 모듈을 만들어서 import 하다보면 

    ImportError: attempted relative import with no known parent package

    위 에러를 많이 만나게 된다.

     

    패키지 안에서 스크립트 파일(실행 파일)을 실행 시키려고 했을 때 발생하는 에러이다.

    일단 이유는 파이썬의 인터프리터가 __main__ 의 위치를 알지 못하기 때문에 위 같은 에러가 발생하는 것이다.

    파이썬의 인터프리터는 relative import의 모듈 위치를 정할 때(기준이 되는 위치) __name__ 속성에 의해 결정되고

    터미널에서 파이썬을 직접 실행시키면 __name__ == '__main__'이 되기 때문이다.

     

     

    이 부분이 이해가 잘 안된다면,

     

    먼저 모듈과 패키지의 차이에 대해서 이해해야 한다.

    모듈과 패키지는 둘다 import를 불러와 쓰는데, 패키지는 모듈을 포함시켜놓은 것이다.

     

    모듈(Module) : 각종 변수, 함수, 클래스를 담고 있는 파일로 특정 기능을 .py 파일 단위로 작성한 것

    패키지(Package) : 여러 모듈을 묶은 것이며 패키지는 모듈에 네임스페이스(namespace)를 제공함

     

    파이썬 3.3 이하에서는 폴더가 패키지임을 명시하기 위해 __init__.py 파일을 폴더 안에 생성해야 했다. 

    3.3이상에서는 __init__.py 가 없어도 패키지임을 파이썬이 인식하지만 되도록 작성해주는 것이 좋다.

     

    패키지안의 모듈을 불러오는 방법은 

    import pacakge.module

    import package.module1, package.module2

    package.module.변수

    package.module.함수()

    pacakge.module.클래스()

    가 있다.

     

     

     

    다음과 같은 구조가 있다고 할 때 main.py에서 utils 패키지의 utils 모듈(utils.py)를 가져와서 사용한다고 해보자.

    __init__.py에 아무것도 쓰지 않았을 때 utils 모듈을 가져오려면 from utils import utils 처럼 패키지와 모듈을 모두 명시해주어야 한다.

     

    # utils/__init__.py
    
    from . import utils

    __init__.py에 위와 같이 명시해주면 import utils만 불러와주고 utils.py안에 hello()라는 함수가 있다고 할 때 해당 함수를 바로 사용할 수 있게 된다. 패키지를 불러올 떄 (import utils) __init__.py 파일이 자동으로 실행되므로 from . 을 통해 현재 패키지에서 모듈을 가져오라고 인식할 수 있게 되는 것이다.

     

    import utils
    
    print(utils.utils.hello()) # utils 패키지의 utils 모듈(utils.py) 안의 hello() 함수 사용

     

     

    이때 utils.py의 __name__ 변수에는 'utils'가 들어가 있고 utils 모듈을 불러오는 main.py의 __name__에는 '__main__'이 들어가 있게 된다. 파이썬의 경우 import 할 때 import 하는 모듈을 모두 실행하기 때문에 원하지 않는 코드가 실행될 수 있는 경우가 있다.

    따라서 시작점(entry point)가 어떤 파일인지 명확히하기 위해 if __name__ == '__main__'을 통해 스크립트 파일을 실행시키는 것이다. 즉 if __name__ == '__main__' 는 현재 스크립트 파일이 프로그램의 시작점이 맞는지 판단하는 역할을 한다.

     

     


     

    해결 방안

     

     

    에러가 발생했을 것 같은 상황은 패키지 안에서 스크립트 파일(실행파일)을 실행했을 때이다.

    예를 들어 pacakge/calculation.py에서 package/package2/add.py안의 add 함수를 불러왔을 때 entry point를 찾지 못한다.

     

    # package/calcuation.py
    
    from .package2.add import my_add
    
    result = my_add(1,2)
    print(result) # ImportError

     

     

     

    1. python의 -m 옵션 이용

    -m 옵션은 모듈을 스크립트로 수행할 때 쓰는 옵션인데 이를 통해 패키지 구조를 알려주면 파이썬이 상대경로를 찾을 수 있게 된다.

     

    ~/test/ > python3 -m package.calcuation

     

     

     

    2. python의 절대경로 이용

     

    # package/calcuation.py
    
    if __name__ == '__main__':
    	if __package__ is None:
    		import sys
    		from os import path
    		print(path.dirname( path.dirname( path.abspath(__file__) ) ))
    		sys.path.append(path.dirname( path.dirname( path.abspath(__file__) ) ))
    		from package.package2 import add
    	else:
    		from .packge2 import add
    
    	result = my_add(1,2)
    	print(result)

     

    코드를 위와 같이 작성하고 pacage/caculation.py 파일을 실행하면 된다.

     

     

     

     

     

     

    References

     

    https://myjorney.tistory.com/52

    https://dojang.io/mod/page/view.php?id=2448

    https://dodonam.tistory.com/107

    댓글

Designed by Tistory.