Module
- 변수, 함수, 클래스 등을 모아놓은 파일
- 다른 파일에서 재사용할 수 있음
- 긴 코드를 여러 파일로 나누어서 정리하기 위해서!
예를 들면, 우리가 사용하는 len( ) 함수는 이미 모듈로 구현이 되어있어서 우리가 쓸 수 있는 것!
모듈은 이미 만들어진 것을 쓰는것도 가능하지만, 우리가 직접 만들수도 있다
## my_module.py
my_module_var = 7
def my_module_func():
return "Hello!"
class MyModuleClass:
pass
👆🏻 요로케 파이썬 파일을 만들어 코드를 저장한 뒤
다른 파일에서 이 모듈을 import 하여 사용하면 된다.
<모듈 이름>.<모듈에서 사용하길 원하는 변수/함수/클래스 이름>
import my_module
어느 파일의, 어느 클래스의 함수나 변수를 쓸 것인지 알 수 없으므로
모듈 앞에 이름을 붙여주어야 한다(name space 구조!)
👇🏻 모듈의 구조와 작동하는 순서!
모듈 import 하는 방식!
1. import 키워드만 사용
2. import fromfrom <모듈 이름> import <함수/변수/클래스1>, <함수/변수/클래스2>, ..., <함수/변수/클래스N> - from my_module import my_module_func, my_module_var print(my_module_var) my_module_func()
: 모듈에서 사용하는 것이 명확할 때 사용!
(하지만 local scope를 가지고있는 변수들과 이름 충돌이 날 수 있으므로 권장하지 X)
3. import asfrom my_module import my_func as f1 from my_module2 import my_func as f2 from my_module3 import function_with_name_too_long as f3 - f1() f2() f3()
: import해오는 모듈의 요소를 원하는 이름으로 사용하는 것!
import my_module as m1 - m1.my_module_func()
: 모듈 이름도 지정할 수 있다!
import pkg.mod1
from pkg.mod2 import func2
pkg.mod1.func2()
func2()
__init__.py
파일을 사용해서 package 사용 시 초기 설정을 해줄 수 있다# __init__.py
from .mod1 import func2
# main.py
from pkg import func2
func2()
👆🏻 이렇게 하면 함수 이름을 곧바로 호출할 수 있다.
__all__
: package를 통해 import될 수 있는 모든 요소들 정의# __init__.py
from .mod1 import func2
from .mod2 import func3
__all__ = ['func2', 'func3']
# main.py
from pkg import *
func2()
func3()
func4() ## <== Error. func4 함수는 __all__ 에 정의되지 않았으므로 import 될 수 없음.
👆🏻 String 값의 요소를 가진 list 이므로
import되길 원하는 요소들을 string으로 list에 선언해주면 된다.
그렇다면 파이썬이 모듈, 패키지를 import하는 방식은 무엇일까?
어떻게 검색하는 것일까?
import abc
👆🏻 요로케 import 하지만, 파이썬이 모듈/package를 어떻게 찾는지는 모른다
어떤 원리로 찾는걸까?
- sys.modules
- built-in modules
- sys.path
파이썬이 가장 먼저 확인하는 곳!
- 이미 import된 모듈, package를 저장하고 있는 단순 dictionary
- sys.modules를 통해 한번 impor된 모듈, 패키지는 다시찾지 않아도 ok!
- 그렇기 때문에 새로 import하는 모듈은 sys.modules에서 찾을 수 X
- 파이썬에서 제공하는 파이썬 공식 라이브러리!
- Built-in 모듈들은 이미 파이썬에 포함되어 있어서
쉽게 찾을 수 있다
- 마지막으로 확인하는 장소
- sys.path는 string 요소들을 가지고 있는 list형태
['', '/Users/song-eun-u/anaconda3/bin', '/Users/song-eun-u/anaconda3/lib/python36.zip', '/Users/song-eun-u/anaconda3/lib/python3.6']
👉🏻 파이썬은 list의 경로를 하나하나 확인하면서 해당 경로에 import하고자 하는 요소가 있는지 확인한다.
- sys는 파이썬에 포함되어있는 모듈이므로,
sys 모듈을 import해서 sys.modules, sys.path를 출력, 수정할 수 있다. (👇🏻 요로케!)import sys - print(sys.path) print(sys.modules)
파이썬은 이렇게 총 3단계를 거쳐서 import하고자 하는 모듈, 패키지를 확인하고,
못찾으면 ModuleNotFoundError를 리턴한다
built-in 모듈과 pip를 통해 설치한 외뷰 모듈 모두
윗 3단계에서 찾을 수 있지만
직접 개발한 local package를 import 할 때에는 해당 위치에 맞게 경로를 잘 선언해야 한다.
└── my_app
├── main.py
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── __init__.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py
👆🏻 프로젝트 구조가 다음과 같다고 가정하고 정리하도록 하자
Absolute path : 절대경로
- import하는 파일, 경로에 상관없이 항상 경로 동일
from package1 import module1 from package1.module2 import function1 from package2 import class1 from package2.subpackage1.module5 import function2
- 경로들의 시작점이 전부 프로젝트의 최상위 프로젝트인 my_app에서 시작한다.
- 리눅스는 slash(/), 윈도우는 back slash(), 파이썬은 dot(.)을 사용해서 표기
Relative path : 상대경로
- import하는 위치를 기준으로 경로 정의
- local package에서 다른 local package import 할 때 사용
# package2/module3.py - from . import class1 from .subpackage1.module5 import function2 - # subpackage1/module5.py from ..module4 import class4
- dot . 하나는 선언되는 파일의 현재 위치
dot .. 두개는 현재위치에서 상위 디렉토리로 가는 경로
Relative paths는 Absolute path에 비해 경로 길이가 짧지만, 헷갈리기 쉽고
파일 위치가 변경되면 경로위치 바꿔줘야 한다는 단점이 있다!
프로젝트 만들기
- init.py 파일에는 아무코드도 없지만 init 파일은 해당 디렉토리가 패키지임을 알려주는 역할을 합니다.
👇🏻 main.py# absoulte path #from calculator.add_and_multiply import add_and_multiply - # relative path 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 # from calculator.multiplication import multiply def add_and_multiply(a,b): return multiply(a,b) + (a+b)
👇🏻 multiplication.py
def multiply(a,b): return(a*b)
- main.py에서 상대경로로 add_and_mutiply 를 임포트 했을 때 발생하는 에러를 확인하고
- 다음의 파이썬 공식 문서를 참고해서 main module 에서는 패키지의 모듈을 어떻게 임포트 해야하는지 블로깅 해주세요.
파이썬 공식 문서
'no parent package'라고 뜬다!
아무래도 상대경로로 했기 때문에 이런 문제가 뜨는 것 같은데..
공식문서를 참조해서 찾아보았다 main.py 파일만의 룰이 있나?
👆🏻 main 모듈의 이름은 항상 __main__.py
라고 하길래, 파일명을 바꿔줬더니 1도 영향 없고 파일명이 바뀐채로 같은 에러가 나온다
아무래도 상대경로 그 자체가 문제인 것 같다는 생각이 들던 차..
👆🏻 알고보니 공식문서 마지막부분에 main module은 무조건 절대경로로 하라고 친절하게 알려주고 있었다.떠먹여줘도 모르는 하람아
main.py 파일 내부에 패키지 앞에 붙은 상대경로 표시를 없애주었더니
그제서야 멀쩡하게 돌아간다!
- add_and_multiply.py에서 multiply함수를 절대경로와 상대경로도 각각 임포트 해보고 main 모듈과 차이점을 생각해보고 결과를 출력해 보세요.
👆🏻 우선 상대경로로 import 했더니 에러가 뜬다
👆🏻 no known parent package라고 해서 패키지명을 명시해 주었는데도 import가 되지 않는다.
👆🏻 절대경로로 패키지명을 명시해주어도 에러! 노 모듈!!
왜?
👆🏻 절대경로는 current directory에서 시작되는데, 무조건 최상위 디렉토리에서 시작하지 않기 때문에
해당 스크립트가 속한 경로 중에서 default로 지정되는 current directory를 파악하면 된다.
나의 경우, 절대경로의 시작점은 calculator 패키지 directory이므로 이를 제외한 절대경로로 명시해주었더니 정상 작동했다!