TIL #28-3 - Python : Repl.it 공부5 - Module

채록·2021년 1월 15일
0

짜투리

목록 보기
19/26
post-thumbnail

들어가는 말

마지막 Python Repl.it 과제로 module에 관해 이해하고 있는지 각 개념에 대한 여러 문제를 풀어야 한다!

이전에 교재[혼공파]를 통해 공부하고 정리한 포스트를 참고하였다. https://velog.io/@c_hyun403/TIL-29-%ED%98%BC%EA%B3%B5%ED%8C%8C-%EB%AA%A8%EB%93%88#1-pip--%EC%99%B8%EB%B6%80-%EB%AA%A8%EB%93%88%EC%9D%84-%EC%84%A4%EC%B9%98%ED%95%A0%EB%95%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EB%AA%85%EB%A0%B9%EC%96%B4




I. sys.modules / built-in modules / sys.path

위 명칭은 module과 pakage를 import하는것을 이해할때 그 개념이 필요하다. Python이 모듈과 pakage를 찾는 경로는 다음과 같다.

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

위 순서대로 찾는다.

1. sys.modules ?


Python이 module / package를 찾기 위해 가장 먼저 확인하는 곳으로 sys.module은 단순한 dictionary이다. 또한 이전에 import된 module / package (이하 m/p) 들을 저장하고 있다. 이로 인해 python에서는 한번 import된 기록이 있는 m/p는 또 찾지 않게 해준다.

이 말은 즉, 이전에 import된 적이 없는 m/p는 sys.module에서 찾을 수 없다.

sys.modules 출력 결과


2. built-in modules ?


Python 에서 제공하는 공식 라이브러리들 이다. 이는 이미 python에 포함되어 나오므로 쉽게 찾을 수 있으며 다음과 같은 기능이 이에 해당한다.

built-in modules 예시
위의 sys.modules 출력 결과를 통해 확인할 수 있다.


3. sys.path ?

sys.path는 string요소들을 갖고 있는 list이다.
또한 pip (외부 모듈을 설치할때 사용하는 명령어)을 통해 설치한 모듈도 이곳을 통해 찾게 되며, 새로게 만든 모듈을 이곳에 경로를 등록해 찾게끔 설정해 준다.

sys.path 출력 결과


Q1) sys.modules 과 sys.path 의 차이점

차이점을 비교면

  1. sys.modules은 dictionary 이지만 / sys.path는 list 형식이다. (각각의 출력결과를 통해 {}와 []의 차이를 확인 할 수 있다.)

  2. sys.modules은 python이 import된 m/p를 찾는 첫번째 장소이고 / sys.path는 가장 마지막 장소이다.

  3. sys.module은 이전에 import된 기록이 있는 m/p만 찾을수 있고 / sys.path는 해당 경로에 있는 모든 m/p를 찾을 수 있다.

Q2) python이 sys 모듈의 위치를 찾는 법

 두가지 모두 sys. 로 시작한다는 점은 같다. 여기서 sys는 Python에 포함되어 있는 모듈이다 따라서 Q2에 해당하는 답변으로는 이미 내장되어 있다라고 답할 수 있다.
또한 sys 모듈을 import하여 .module과 .path를 출력/수정할 수 있다.




II. Absolute path / relative path

python에서 built-in 모듈과 pip을 통해 설치한 외부 m/p는 import 하는데 큰 어려움이 없을 것이다.
왜냐하면 built-in은 당연히 python 에 내장되어 있으므로 그 경로가 확실하니 잘 찾아질 것이고, pip을 통해 설치한 모듈도 자동으로 site-packages라는 directory에 설치되기 때문이다. 참고로 이 site-packages는 이미 sys.path에 포함되어 있다.

내 컴퓨터에도 site-packages가 있다.


1. Absolute path ?

Absolute path는 말 그대로 절대 경로이다. 절대 경로라고 하는 이유는 import를 하는 파일이나 경로에 상관없이 항상 경로가 동일하기 때문이다.

위와 같이 my_app 이라는 프로젝트 안에 package1과 package2가 있을때 각 package 안의 module을 import 하는 방식은 다음과 같다.

from package1 import module1
from package1.module2 import function1
from package2 import class1

위 상황처럼 현재의 프로젝트의 가장 최상위 디렉토리에서 시작한다.

한가지 더 주목할 점은 위 예시중에서 package2 안에 subpackage1라는 중첩 package 형태가 있다. subpackage1의 module5의 function2 함수를 Import 하기 위한 경로는 다음과 같다.

my_app -> package2 -> subpackage1 -> module5.py

import 하기위한 코드는 다음과 같다

from package2.subpackage1.module5 import function2

경로가 매우 길다.!! 이 점을 보완하기 위해 relative path를 사용할 수 있다.


2. relative path ?

relative path는 absolute path와 달리 해당 프로젝트의 최상단 디렉토리를 기준으로 경로를 잡는것이 아니라 import 하는 위치를 기준으로 경로를 정의한다. 따라서 relative path는 일반적으로 local package안에서 다른 Local package를 import 할때 사용된다.


위와 같은 myapp 프로젝트 디렉토리를 사용중일때를 예시로 들어보자.
예를 들어, package2의 module3에서 _package2의 class1
subpackage1의 module5의 function2 함수를 import 하려면 다음과 같이 작성해야 한다.

# package2의 module3 위치에 있음
# package2/module3.py

# package2의 class1
from . import class1 
# subpackage1의 module5의 function2
from .subpackage1.module5 import function2

여기서 dot (.)import가 선언되는 파일의 현재 위치를 의미한다.
또한 dot 2개 (..)현재위치에서 상위 디렉토리로 가는 경로이다.

Absolute path의 코드와 비교하면 확연히 짧아진 길이를 확인할 수 있다!


Q3) abspath / realpath 차이점&사용시 주의


Absolute path (abspath)

  • 현재의 프로젝트 디렉토리 (current directory)는 default값으로 sys.path에 포함되게 된다. 때문에 absolute path는 current directory로부터 경로를 시작한다.
  • 이 때문에 일반적으로 local package를 Import 할때에는 absolut path를 사용하면 된다.

relative path (realpath)

  • 선언해야 하는 경로의 길이는 확실히 줄여준다. 하지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 같이 변경되어야 한다.

따라서, 왠만한 경우 absolute path를 사용하는것이 권장 된다.




Q4) calculator 패키지 만들기

현재 사용하고 있는 python 개발도구는 vs code이다.

목표는 calculator 라는 패키지를 만들고 main.py에 무사히 import 시키는 것이다.


1. 틀 작성


2. 코드 작성

1) 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))

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)

3) multiplication.py

def multiply(a,b):
    return(a*b)

3. 외부모듈로써 실행시키기

terminal을 열어 pip3 install calculator을진행했다!. 이제 site-padkages 에 들어가면 내가만든 calculator가 존재한다 ㅎㅎㅎ


4. 실행하기

출력해보자!! 원하는 결과는 5 이다.

Q5) 에러 확인 / 해결하기

!!!!! 오류 발생 !!!!!

친절한 vs code기능덕에 아직 실행하기도 전에 오류가 난것을 확인할 수 있었다..

relative path 로 import를 할 경우 import 하는 위치를 기준으로 경로를 정의한다. 때문에 __name__=='__main__'을 유의해서 봐야 한다.
여기서 main.py는 calculator폴더에 속한것이 아니기 때문에 main.py에서 __main__은 main이 된다.

그럼, if 조건문을 없애고 바로 add_and_multiply함수가 담긴 print를 출력하면 되느냐?? 그것도 아니다.

ImportError: attempted relative import with no known parent package

라는 문구와 인사할 수 있다. 결론은 relative path가 잘못된 선택이라는 것이다.


해결중..


1. 절반만 성공

이 방법을 해결하기 위해선 absolute path를 사용하면 된다.

그래서 abspath로 변경 후 실행을 했는데..되긴하는데...
저 2는 무엇인가.

이를 해결하기 위해선 __name__ == '__main__'의 의미를 알아야 한다. https://velog.io/@c_hyun403/TIL-38-namemain-%EC%9D%98-%EC%9D%98%EB%AF%B8 말이 너무 길어져서 따로 정리했다.

위 개념을 참고하여 multiplication 파일을 다음과 같이 수정하였다.

2. 완벽한 출력!

기나긴 싸움이었다...


5. 해결 포인트

  1. relative path 가 아닌 absolute path
  2. if __name__='__main__' 의 활용도

두가지,, 잊지말고! 복습하고! 이해하고!



6. 문제와 상관없던 에러상황

하.. 폴더 안의 폴더에 들어있는 파일을 vs code의 터미널을 통해 실행하려는데 더럽게 안됐다. (이건 내가 착각하고 main.py 파일을 새로운 폴더에 집어넣어서 만들었을때 나타났던 현상이다. - 파일1번 안에 caculator폴더와 폴더2번이 있고, 폴더2번안에 main.py 파일이 있던 상황)

왜!! 자꾸!! 파일을 못찾냐고!!!!!!!!!! 근데 또 터미널 말고 Run and Debug로 실행하면 된다. 무슨 상황 대체?!?!

전혀 연관성은 없는거같지만... 지금까지 블로깅을 써내려가면서 absolute / relative path를 공부하며 현재위치,, 외부위치,, 이말을 많이 반복학습했다.

뭔가 여기서 설마...? 싶어서 폴더 안의 폴더 (이하 폴더2 라고 한다.)로 이동시키기 위해

cd 폴더2

를 하고나니 터미널에서의 위치가 폴더2로 바뀌는걸 확인할 수 있었다. 그 후 폴더2에 있는 파일을 터미널로 실행하니 잘됐다. 후.....





Q6) add_and_multiply.py에서 multiply함수를 절대경로와 상대경로도 각각 임포트 해보고 main 모듈과 차이점을 생각해보고 결과를 출력해 보세요.

난... 둘다 안되는데....?

안되는건 내 잘못인거같다. 원래 이론대로면 둘다 무사히 결과가 출력되어야 한다.

1) 절대경로로 출력했을때.

절대경로로 출력하는 코드는 다음과 같다.

from calculator.multiplication import multiply

"python_module이라는 같은 프로젝트에서 단순하게 calculator 폴더안에 있는 mutiplication 파일에서 mutiply 함수를 호출한다" 라고 하는 것이다.


2) 상대경로로 출력했을때.

상대경로로 출력하는 코드는 다음과 같다.

from .multiplication import multiply

짧다!
상대경로는 import 하는 위치를 기준으로 삼는다. 현재 파일이 add_and_multiply.py로 multiplication.py와 같은 폴더 안에 존재한다. 때문에 dot(.)을 이용한 것이다.



둘다.. 잘.. 출력되야하는데 난 왜 안되...?
1) 상대경로

2) 절대경로





III. __init.py__ 파일이란 ?

간혹 package가 import될때 초기 설정이 필요한 경우가 있다. 이럴때 python은 __init__파일을 사용한다.


packag안에 __init__파일이 있으면 해당 package가 import될때 __init__파일의 코드들이 자동으로 실행된다. 이러한 점을 이용하여 다음과 같은 일이 가능하다!!!

    1. import할때 경로의 총 길이 줄이기
    1. package에서 import할 수 있는 변수/함수/클래스 제한하기
    1. 그 외 package가 import될 때 꼭! 먼저 실행되어야 하는 코드 설정해주기

1. import할때 경로의 총 길이 줄이기


다음과 같은 package가 있다 가정해보자.
이런상황에서 mod1의 func2라는 함수를 import하여 사용하기 위해 다음과 같이 코드를 작성해야 한다.

# main.py
import pkg.mod1
pkg.mod1.func2()

길다!!!! func2 함수를 호출할 때마다 매번 경로를 쫘르륵 입력해주어야 한다니.. 번거롭다!

이럴때 __init__.py파일을 사용하자. __init__.py파일에 다음과 같은 코드를 작성한다.

# __init__.py
from .mod1 import func2

이렇게 되면 func2 기능을 불러올때 다음과 같이 코드를 작성해주면 된다.

# main.py
from pkg import func2
func2()

2. import 할 수 있는 변수/함수/클래스를 제한할 수 있다.

import했다 하더라도 그 안의 모든것이 노출되면 안될수도 있다. 내부적으로만 작동해야 하는경우도 있을텐데 이럴 때 __init__.py파일이 기능한다. 그리고 이런 경우 사용하는 것이 __all__ 변수 이다.

1) __all__ 변수

__all__ 변수는 string 값의 요소를 갖고 있는 list로 이 값의 default값은 모든 함수/변수/클래스 이다. 때문에 __init.py__파일에 들어있는 __all__ 변수를 특별히 정의함으로써 import 될수 있는 요소들을 빠르게 제한할 수 있는 것이다.

2) 제한하기

다음의 예시를 통해 이해할 수 있다.

# __init__.py
from .mod1 import func2

__all__ = ['func2']

# main.py
from pkg import *

func2()
func3()
-> func3() 에러발생!!!!!!!!!!!!!

func3 함수는 __all_ 변수에 정의되지 않았으므로 import 될 수 없다!


3. 먼저 실행시킬수있는 코드들

간단하다! package 파일을 import 했을때 __init.py__파일속의 코드들이 자동으로 실행된다. 이 점을 이용해 package import 후 다른 기능을 사용하기 전에 __init.py__파일속의 코드를 실행시킬 수 있다.





Q7) init.py 파일의 역할

위에 다 설명했지만 요약하자면
package를 import했을때 자동으로 실행되는 파일로 이 파일의 코드를 작성함으로써 기능할수있는 함수/변수/클래스를 제한할수도 있고, 작성하는 총 경로를 줄일 수 있으며, 다른 기능이 실행되기 전 먼저 기능할 수 있도록 코드를 작성할 수 있다.

profile
🍎 🍊 🍋 🍏 🍇

0개의 댓글