Python - 직접 패키지를 만들어 import해보자! + ImportError

solee·2022년 1월 31일
0

Python

목록 보기
4/16

귀여운 과제를 받았다. 직접 패키지를 만들어 보고, 그 패키지를 임포트하며 상대 경로와 절대 경로를 다양하게 시도해 보는 것이다.

만들 패키지의 형태는 이렇다.

  • main.py
from .calculator.add_and_multiply import add_and_multiply

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

  • add_and_multiply.py
from .multiplication import multiply

def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)

  • multiplication.py
def multiply(a,b):
    return(a*b)

  • __init__.py에는 아무 것도 적지 않는다.

간단하게 살펴보자. main.py에서 add_and_multiply.py에 있는 함수 add_and_multiply를 import한다.

add_and_multiply.py를 보면, 여기서도 multiplication.py에서 import를 하고 있는 것을 확인할 수 있다. add_and_multiply 함수의 결과값은 a와 b를 곱한 값과 a와 b를 더한 값을 다시 더하는 것인데, 여기서 곱하는 함수를 multiplication.py에서 가져온다.

multiplication.py에서는 a와 b의 곱을 리턴한다.


모든 것이 좋아 보인다.
그런데 어라? 에러가 발생한다!



ImportError

ImportError: attempted relative import with no known parent package


먼저 살펴야 할 것이 있다.

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

main.py에 있던 이 if문이 기억나는가? 이런 의문이 들어야 한다.

__name__이 뭐지?
그리고 __main__은 뭐지?

왜 이 둘이 같아야 하는 거지?

다시 차근차근 알아보자. 이전 글에서 import에 대해 절대 경로와 상대 경로를 살폈다. 그리고 파이썬이 모듈의 위치를 어떻게 확인하는지에 대해서도 알아보았다.

__name__는 변수다. 어? 우리는 main.py에 이런 변수를 선언한 적이 없다! 그러니 우리는 __name__가 파이썬 내장 변수임을 알 수 있다. 그리고 __name__모듈의 이름을 담는다.


__name__ 이라는 변수는 파일이 실행될 때 해당 파일의 이름을 가진다.

만약 dog이라는 모듈을 임포트했다면, dog.py에 __name__이라는 코드가 있고 그 코드를 출력한다면 dog이 되는 식이다.


  • main.py
import aaa
 
print("main.py에서 __name__ :", __name__) 
print(aaa.hotdog())
  • aaa.py
def hotdog() :
    print("사실 나는 핫도그가 좋다")
    return "핫도그와 " + __name__
    
print("나는 자유로운 도비예요!")

이렇게 있다면, import할 때에 aaa.py에 있는 코드가 실행이 되고 그 후에 main.py에 있는 코드가 실행될 것이다. 결과는 아래와 같다.

나는 자유로운 도비예요!
main과  __main__
사실 나는 핫도그가 좋다
핫도그와 aaa

똑같은 __name__이 어떤 파일에 쓰여있느냐에 따라 다르게 출력되는 것을 알 수 있다. 또 다른 모듈을 사용할 때에는 이름이 그것으로 바뀌었지만, main.py에서 사용할 때에는 __main__이 된 것도 확인할 수 있다.

__name__은 실행한 시작점을 __main__으로 잡는다.

그러면 다시 아까의 ImportError로 돌아와 보자.

ImportError: attempted relative import with no known parent package

상대 경로는 현재 실행하는 파일의 위치를 기준으로 계산된다. 파이썬이 인식하는 현재 위치가 __main__으로 인식된다면? 파이썬은 이 main이 어디 있는 main인지 알 수 없다!
그러므로 main.py를 이렇게 바꾼다면

from .aaa import hotdog

print("main과 ", __name__) 
print(hotdog())

파이썬이 __main__이 어디인지 확인할 수 없게 되므로 ImportError가 발생하게 된다. 이 경우 상대 경로가 아닌 절대 경로를 이용하거나, import 구문에서 from 키워드를 사용하지 않아야 한다.

혹은, 패키지 내부의 __init__.py 파일에

import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

이런 문장을 넣는다.



그러면 aaa.py에 if문을 넣으면 어떻게 될까?

if __name__ == '__main__':
	print("나는 자유로운 도비예요!")

if __name__ == '__main__': 는 해당 코드가 파일의 시작점이 어디인지 파악하는 용도로 사용된다. 또 아까의 print()구문이 그냥 출력되지 않게 할 수도 있다. 파이썬에서 어떤 파일이든 모듈이 될 수도 있고 메인 실행 파일이 될 수도 있기 때문에 이 if문을 통해 현재 실행하는 위치가 메인 실행 파일이 아닐 경우, 즉 모듈로 사용될 경우 출력되지 않게 할 수 있는 것이다.

위의 도비 문장은 aaa.py를 직접 실행시킬 때, 즉 aaa.py가 __main__일 때에 실행될 것이다.



그리고 또 하나 신경써야 할 점이 있다. 바로 "나는 자유로운 도비예요!"다. 이 print()는 hotdog()함수보다 더 빨리 실행된 것을 알 수 있다. 심지어 main에서 print해 출력한 __main__보다 빨리 실행되었다!

왜냐하면 도비 문장은 main이 aaa를 import할 때에 실행되었기 때문이다. 파이썬이 import할 때에 해당 모듈에 있는 모든 코드를 실행하기 때문에 그 안에 있는 클래스, 함수, 변수를 인식할 수 있다. 변수 선언을 먼저 하지 않으면 그 변수를 사용할 수 없는 것과 마찬가지다.




다시 맨~위의 문제로 돌아가자.

  • main.py
from calculator.add_and_multiply import add_and_multiply 

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

  • add_and_multiply.py
from .multiplication import multiply

def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)

  • multiplication.py
def multiply(a,b):
    return(a*b)

  • __init__.py에는 아무 것도 적지 않는다.

간단하게는 main.py에서 import를 절대 경로로 사용하거나, 아까 언습한 코드를 __init__.py에 입력하는 방식이 있다.

실행시켜 보면 무난하게 결과가 나온다.






마치...지 않으며

그러면 이 때, 하나 더 궁금해진다.

그래서 __init__.py은 뭔데?

__init__.py파일은 기본적으로 텅 비어 있으며, 패키지 초기화 파일이기도 하고 파이썬이 이 파일이 있는 디렉토리를 패키지로 인식하게 만든다. 파이썬 3.3 이후로는 이게 없어도 된다는데, 하위 버전과의 호환과 안정성을 생각하면 넣어주는 것을 권장한다고 한다.

여러 모듈과 __init__.py파일을 한 디렉토리에 집어넣은 후 import에 대한 속성을 정의해 해당 속성을 보다 편히, 상대 경로를 사용해 import할 수 있게 해 주기도 한다.

반대로 이 파일에 어떤 함수나 변수를 import할지 지정해 두면, import *을 사용하더라도 파일이 통째로 import되지 않고 지정한 것, 원하는 것만 드러낼 수도 있다고 한다.

profile
DA DA DA

0개의 댓글