파이썬을 공부하면서 모듈은 많이 만들어봤지만 패키지를 만들어 본 경험을 거의 없다. 패키지 다운로드는 많이 받아봤다 ㅎㅎㅎ. 사전스터디때 파이썬 문법을 공부해서 제법 잘 안다고 생각했지만 막상 읽어보니 머리가 약간 어질하다. 열심히 해보겠습니다 🙇.
"둘 다 sys 모듈을 import 해야함다!!"라고 외치며 공통점이 먼저 눈에 들어오지만 여기서는 차이점을 알아봐야한다.
전체적으로 sys 모듈에 대해 정리해 놓은 페이지가 있다. 텐션이 떨어져있기 때문에 생각과 요약을 잠시 접어두고 적혀있는 그대로 번역을 해보겠습니다 (사실 짧다ㅋㅋㅋ).
sys.modules의 값은 이미 로드된 모듈이 그 이름과 쌍을 이루는 딕셔너리입니다.
로드된 모듈을 다시 불러오는 일이 없도록 관리하기 위한 딕셔너리라고 생각한다.
sys.path는 파이썬 라이브러리들이 설치되어 있는 디렉터리들을 보여준다. 만약 파이썬 모듈이 위의 디렉터리에 들어 있다면 모듈이 저장된 디렉터리로 이동할 필요없이 바로 불러서 사용할 수가 있다. 아래는 sys.path를 인터프리터에서 실행했을 때 결과이다.
['',
'/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload',
'/Users/choonghee/Library/Python/3.7/lib/python/site-packages',
'/usr/local/lib/python3.7/site-packages']
리스트 형태이다. 그렇다! 필요한 모듈이 있는 경로를 append()
함수로 추가할 수 있다!!
파이썬이 모듈을 확인할 때,
sys.modules는 파이썬이 제일 먼저 확인하는 장소이며 Dictionary이고
sys.path는 파이썬이 제일 마지막에 확인하는 장소이고 List이다.
PyMOTW-3 페이지를 보고 영감을 얻었다. 참고로 PyMOTW란 Python Module of the Week이다.
파이썬 인터프리터는 일부 내장된 C 모듈과 함께 컴파일 된다. 그래서 해당 모듈들은 따로 분리된 라이브러리일 필요가 없다. 이러한 내장 모듈들은 엄밀히 말하면 import 되는게 아니기 때문에 sys.modules
에 나타나지 않는다. 내장 모듈들의 이름을 보고싶다면 다음과 같은 코드를 사용한다.
import sys
for name in sys.builtin_module_names:
print(name)
그럼 다음과 같은 결과가 나타난다.
_abc
_ast
_codecs
_collections
_functools
..
중간생략
..
posix
pwd
sys
time
xxsubtype
zipimport
결론은 인터프리터에 이미 내장되어 있으므로, 인터프리터를 실행하면 그냥 로드된 것이다!
위의 타이틀만 읽어보면, 리눅스에서 명령어에 경로를 써줘야할 때가 생각난다. 파이썬에서는 무엇을 의미하는지 궁금하다!
Absolute imports는 프로젝트의 루트 폴더에서 부터 full-경로를 사용하여 import될 리소스를 명시하는 것이다.
당신은 다음과 같은 디렉토리 구조를 가지게 됩니다 후후... 😈
└── project
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
당신은 아래의 것들을 코드로 표현해야 합니다 후후... 😈
코드로 표현해보자면
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2
여기서 눈여겨 봐야할 점은 각 패키지 또는 파일의 상세한 경로를 탑-레벨 패키지 폴더에서 부터 지정해줘야 한다는 점이다. 파일 경로와 비슷한 점이 있지만 슬래쉬(/)대신 점(.)을 사용해야한다.
장점! 일단 명쾌하게 import된 리소스의 위치를 파악할 수 있다. 사실, PEP8은 absolute imports를 명시적으로 추천한다!
하지만.. 디렉토리의 구조가 복잡해지면 살벌해진다 🔪.
from package1.subpackage2.subpackage3.subpackage4.module5 import function6
이런 경우에는 relavtive imports를 사용하자!
relative imports는 현재 위치에 상대적으로 import될 리소스를 명시하는 것이다. implicit한 방법과 explicit한 방법이 있지만 파이썬 3에서 implicit relative imports는 deprecated되었으므로 다루지 않는다.
relative imports 구문은 import될 모듈, 패키지, 객체의 위치 뿐만 아니라 현재의 위치에 따라 달라진다. 위에서 봤던 디렉토리 구조를 다시 사용해 예시를 들어본다.
└── project
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
시작하기전 알아야하는 것은 하나의 점(.)은 현재 위치와 같은 디렉토리의 모듈이나 패키지를 가리킨다. 두 개의 점(..)은 현재 위치의 부모 디렉토리를 가리킨다는 것이다. 세 개의 점을 이용해서 부모의 부모 디렉토리도 가리킬 수 있고 계속 그렇게 점을 늘려가도 되지만, 개인적으로 본 기억이 없다.
# package1/module1.py
from .module2 import function1
module1에서 module2의 function1을 import 한 것이다. 시작하기 전에 말한 점-규칙을 잘 생각해보면 이해할 수 있을 것이다.
relative imports는 당연히 간결한 구문이 장점이다. 위에서 봤던 복잡한 코드는 다음과 같이 간단해질 수 있다.
from ..subpackage4.module5 import function6
하지만 absolute imports에 비해서 가독성이 떨어지고, 다른 사람과 협업시 디렉터리 구조가 바뀔 가능성이 높으므로 오류가 발생할 가능성도 높아진다.
일단 다음과 같은 구조의 프로젝트가 있다.
├── calculator
│ ├── __init__.py
│ ├── add_and_multiply.py
│ └── multiplication.py
└── main.py
그리고 main.py에는 다음과 같은 코드가 있다. 나머지 파일들이 어떤 코드를 가졌는지 생각하지 말고 "산은 산이요 물은 물이로다"하는 마음으로 보면된다.
# relative path
from .calculator.add_and_multiply import add_and_multiply
if __name__ == '__main__':
print(add_and_multiply(1,2))
실행해보면 다음과 같은 오류가 발생한다.
ImportError: attempted relative import with no known parent package
해석해 보면 부모 패키지도 없는데 relative import를 시도했다는 뜻이다. 그럼 main 모듈에서 어떻게 import를 해야할까??
파이썬 문서를 보면 메인 모듈에서 어떻게 import 해야하는지 적혀있다.
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 imports는 현재 모듈의 이름을 기반으로 한다. 메인 모듈의 이름은 항상 "__main__"이기 때문에, 파이썬 어플리케이션의 메인 모듈로 사용하려는 모듈은 항상 absolute imports를 사용해야한다.
뭔 말..?
태현님이 "메인 모듈을 보통 프로젝트의 루트에 있기 때문에 찾을 부모 디렉터리가 없어서 relative imports를 하면 에러가 난다"고 말씀해주셔서 이해가 되었다 😭 Je vous remercie 🥳!!
사전스터디에서 장고로 앱을 생성할 때 항상 자동으로 추가되어있던 __init__.py
... 그 때는 "이게 뭐고? 암것도 없는데 와만드는데" 하고 그냥 대수롭지 않게 넘어갔다. 이제 알아볼 시간이 온 것 같다 ㅋㅋ. 그것의 역할을 몇가지 정리해본다.
__all__
변수를 통해 import 할 수 있는 변수/함수/클래스를 제한 할 수 있다.파이썬 문법 공부할 때 언급자체가 없거나 소홀히하기 쉬운 내용들이라 블로그에 정리한 내용들이 대부분 모르는 내용이었다. 정리되는게 하나씩 생기니 좋다. 위코드에 오길 잘한 것 같다 ㅎㅎ 😂!
아마도 소헌님께서 피드백을 주셨는데 4, 5, 6번 세개나 빠졌다고 하셨다 ㅋㅋㅋ. 나의 정신머리 수준.. 빨리 끝내고 싶은 마음의 소리를 따라갔나보다 🙄🙄.
네? 세 개나 빠졌다구요? ㅜㅜ 다시 열심히 해보겠습니다 🙇🏻♂️
로컬 환경에 파이썬을 설치하고 직접 패키지를 만들어보는 과정이다.
일단 에러를 확인해야하니 확인해본다.
Traceback (most recent call last):
File "/Users/choonghee/workspace/wecode/projets-pour-blogging/calculator-package/main.py", line 5, in <module>
from .calculator.add_and_multiply import add_and_multiply
ImportError: attempted relative import with no known parent package
부모 패키지가 정의되어있지 않은 상태에서 상대 경로로 import를 시도해서 발생한 ImportError다. 이럴 땐 어떻게 해야할까? 사실 위에 적어놨다 (2트).
위에서 적어놓은대로 absolute path를 사용하여 수정해 보았다 (사실 주석 없애기 ㅋㅋ).
결과는
5
성공!
진짜 1차원적으로 repl.it에 적힌대로 생각해보고 결과를 출력하고 "ㅇㅋㅇㅋ"이러고 넘어갔다. 포스팅할 때 정리를 안하다니 ㅜㅜ
from calculator.multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
>>> python main.py
5
from .multiplication import multiply
def add_and_multiply(a,b):
return multiply(a,b) + (a+b)
>>> python main.py
5
add_and_multiply.py
는 calculator
패키지에 있는 모듈이기 때문에 원하는 경로 스타일을 적어넣어 활용하면 된다. 절대 경로이던지 상대 경로이던지 상관없다!
메인 모듈에서 import하는 방법은 위에 적어놨다 🤧 (3트).
포스팅은 피드백 받아서 재수정하는 일이 없도록 해야겠다. 심신이 미약해짐을 느낀다 ㅋㅋ 😪.