귀여운 과제를 받았다. 직접 패키지를 만들어 보고, 그 패키지를 임포트하며 상대 경로와 절대 경로를 다양하게 시도해 보는 것이다.
만들 패키지의 형태는 이렇다.
from .calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
from .multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
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: 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
이 되는 식이다.
import aaa
print("main.py에서 __name__ :", __name__)
print(aaa.hotdog())
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할 때에 해당 모듈에 있는 모든 코드를 실행하기 때문에 그 안에 있는 클래스, 함수, 변수를 인식할 수 있다. 변수 선언을 먼저 하지 않으면 그 변수를 사용할 수 없는 것과 마찬가지다.
다시 맨~위의 문제로 돌아가자.
from calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
from .multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
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되지 않고 지정한 것, 원하는 것만 드러낼 수도 있다고 한다.