[TIL] Modules & Packages

Hailee·2020년 11월 22일
0

[ TIL ]

목록 보기
15/40
post-thumbnail

Modules

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 from

from <모듈 이름> 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 as

from 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()

: 모듈 이름도 지정할 수 있다!


Packages

  • 모듈과 마찬가지, 다른 파일에서 불러와서 사용하는 것
    (대신 더 크고 복잡한 코드)
  • 하나의 모듈에 담기엔 너무 긴 코드일 경우, 여러 파일에 나누어져 있는 코드들을
    다른 곳에서 하나의 모듈로 불러와서 사용할 수 있도록 해주는 것!

    👆🏻 파이썬 파일들로 이루어져 있는 directory가 하나의 package가 된다
    (directory 명 => package 명)
  • 모듈 import 할 때처럼 원하는 패키지의 모듈을 import하면 된다
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될 수 있는 모든 요소들 정의
    : 해당 변수를 사용하면 package 외부에서 import 시 사용되는것을 막을수 있다.
    : default 값이 모든 함수/변수/클래스 이므로,
    따로 정의해주면 => 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에 선언해주면 된다.


How Import Statement Finds Modules & Packages

그렇다면 파이썬이 모듈, 패키지를 import하는 방식은 무엇일까?
어떻게 검색하는 것일까?

import abc

👆🏻 요로케 import 하지만, 파이썬이 모듈/package를 어떻게 찾는지는 모른다
어떤 원리로 찾는걸까?

1) 3steps to find module/package

  1. sys.modules
  2. built-in modules
  3. sys.path

sys.modules

파이썬이 가장 먼저 확인하는 곳!

  • 이미 import된 모듈, package를 저장하고 있는 단순 dictionary
  • sys.modules를 통해 한번 impor된 모듈, 패키지는 다시찾지 않아도 ok!
  • 그렇기 때문에 새로 import하는 모듈은 sys.modules에서 찾을 수 X

built-in modules

  • 파이썬에서 제공하는 파이썬 공식 라이브러리!
  • Built-in 모듈들은 이미 파이썬에 포함되어 있어서
    쉽게 찾을 수 있다

sys.path

  • 마지막으로 확인하는 장소
  • 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를 리턴한다

2) Absolute path & Relative path

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에 비해 경로 길이가 짧지만, 헷갈리기 쉽고
파일 위치가 변경되면 경로위치 바꿔줘야 한다는 단점이 있다!


3) 실습 - calculator 패키지 만들기

프로젝트 만들기

  • 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이므로 이를 제외한 절대경로로 명시해주었더니 정상 작동했다!

profile
웹 개발 🐷😎👊🏻🔥

0개의 댓글