이 글의 작성자는 C/C++ 프로그래밍을 하던 사람이다. 이 글은 Python을 복습하며 작성하는 글이니, 부족한 부분이 있으면 얼마든지 피드백을 주시기 바란다.

 

모듈(module)

Python에서 모듈이란 Python 코드를 담고 있는 파일이라고 할 수 있겠다. 여기서 코드에는 함수가 들어갈 수도 있고, 변수가 들어갈 수도 있고, 나중에 배우게 될 클래스라는 것도 들어있다. 그리고 이러한 파일들은 보통 .py 확장자를 가지고 있다.

지금까지 인터프리터에서 간단하게 돌린 코드도 있긴 하지만, .py 파일에 작성한 코드들도 있었다. 그렇다. 이미 우리는 모듈을 만들고 있었던 것이다!

모듈(module)의 뜻을 영어 사전에서 찾아보면 단순히 모듈이란 독음이 나오기도 하지만, 조립 부품이란 단어가 나오기도 한다. 이 단어가 Python의 모듈, 더 나아가서는 프로그래밍의 모듈을 설명할 수 있는 의미이기도 하다.

프로그래밍을 하다 보면 여러 개의 프로젝트에서 같은 기능을 사용해야 하는 경우가 상당히 많다. 이럴 때 마다 코드를 복사/붙여넣기 하는 것은 이미 함수를 통해서도 비효율적이라고 배웠을 것이다. 특정 코드를 재사용 하기 위해 함수로 묶었다면, 이 함수를 재사용 하기위해 모듈로 묶는다고 생각하면 좋을 것이다.

물론 클래스도 이 내용에 포함되지만(어찌보면 더 큰 내용이지만), 아직은 클래스를 다루지 않았으므로 넘어가자.

 

다른 모듈을 사용하는 방법 : import

import를 통해 다른 모듈을 사용할 수 있다. 먼저 hello 함수만 들어있는 모듈, hello.py 파일을 만들어보자.

hello.py

def hello():
    print('hello!')

그리고 hello.py를 이용하여 hello()를 사용할 모듈, test.py 파일을 만들어보자. 이 때, hello.py와 test.py는 같은 경로에 두어야 한다.

test.py

import hello
hello.hello()

이제 test.py 코드를 실행해보자. ‘hello!’가 출력되는 것을 확인할 수 있을것이다.

hello() 함수를 호출하기 위해 앞에 hello.을 붙이는 것을 볼 수 있다. 이것이 의미하는 것은 hello 모듈의 전역 네임스페이스를 참조하겠다는 의미이다. 만약 test.py에도 hello() 함수가 있고, hello.py에도 hello() 함수가 있다면, 네임스페이스 참조를 통해 구분할 수 있게 된다.

 

특정 요소만 import 하기

하나의 모듈에 여러 기능들이 들어있을 수 있다. 하지만 정작 내가 필요한 기능은 단 한가지 뿐이라면, 굳이 나머지 기능들을 import할 필요는 없다.

이럴 때, 특정 요소만 import 하고자 한다면 다음과 같이 하면 된다.

만약 hello.py에 다음과 같은 기능들이 있다고 치자.

def hello():
    print('hello!')

def nicetomeetyou():
    print('nice to meet you!')

def bye():
    print('bye!')

만약 ‘만나서 반갑습니다!’ 라는 말을 출력하는 nicetomeetyou만 필요하다면, 이렇게 사용하면 된다.

from hello import nicetomeetyou
nicetomeetyou()

from 뒤에는 필요한 기능이 담긴 모듈 이름을 작성하고, import 뒤에는 해당하는 기능(들)의 이름을 작성하면 된다.

기능(들)이라고 적어놓은 것에 주목해보자. 한 번에 여러 기능을 추가할 수도 있다.

from hello import nicetomeetyou, bye
nicetomeetyou()
bye()

import 뒤에 쉼표로 이름들을 구분하면 된다.

 

이름을 바꿔서 import하기

hello.py 모듈을 import 했지만, hello란 네임스페이스가 마음에 들지 않는다면, 사용하는 측에서 새로운 이름을 지어줄 수도 있다.

import hello as hi
hi.hello()

import hello 뒤에 as를 붙인 뒤, 바로 뒤에 자신이 새로 지어주고 싶은 이름으로 바꿔주면 된다. 이러면 이제 hello 모듈의 네임스페이스에 접근하고자 한다면, 자신이 새로 지어준 이름으로 접근하면 된다.
위의 코드에서는 hellohi로 바꿔서 접근하도록 변경하였다.

특정 기능만 import 할 때도 사용할 수 있다.

from hello import nicetomeetyou as nice
nice()

nicetomeetyounice로 바꿔버렸다.

 

import한 내용도 네임스페이스에 들어간다

import한 기능/요소도 결국은 하나의 이름을 가지게 된다. 새로운 이름은 네임스페이스에 들어가게 된다. 이는 import한 요소도 전역/지역 중 어디에 들어갔느냐에 따라 참조가능한 영역이 달라진다는 이야기이다.

def howareyou():
    import hello
    hello.hello()

hello.hello()

위의 코드는 동작하지 않는다. howareyou 함수의 지역 네임스페이스에만 hello가 들어갔고, 전역에서는 hello를 볼 수 없기 때문이다.

변수 선언과 비슷하게 동작한다고 보면 된다.

 

모듈 경로

Python은 모듈을 import 해 달라는 요청을 받았을 때, 아래와 같은 경로에 모듈이 있는지 찾아본다.

  • 내장 모듈
  • 현재 경로
  • PYTHONPATH

만약 import를 하기 위해 탐색하는 디렉토리 목록을 확인하고 싶다면, 다음과 같이 하면 된다.

import sys
print(sys.path)

여기서 내장 모듈이란 단어가 눈에 보일 것이다. 내장 모듈은 Python에서 제공하는 모듈을 의미한다.

 

내장 모듈 사용해보기 : copy

그러면 내장 모듈을 한 번 사용해보자. 기왕 사용해보는 김에, 리스트의 복사에 대해 좀 더 알아보자.

리스트에서 제공하는 copy() 메서드를 사용했었다.

>>> a = [1,2,3]
>>> b = a.copy()
>>> b
[1, 2, 3]

이렇게 하면 b에 새롭게 원소를 추가한다 해도, a의 리스트에는 아무런 영향이 없었다. 하지만 다음과 같은 리스트는 어떨까?

a = [[1,2,3],[4,5,6],[7,8,9]]
b = a.copy()
a[0][1] = 99
print(b[0][1])

ba의 사본이니, a의 리스트의 내용을 바꿨으므로 b의 리스트에는 영향이 없어야 할 것 같다. 하지만 정작 나오는 결과는 99다! 분명히 우리는 copy()를 통해 복사를 했는데, 어떻게 된 일일까?

사실 ab가 가리키는 리스트는 같은 리스트이다. 각각의 항목을 id()로 출력해보자.

a = [[1,2,3],[4,5,6],[7,8,9]]
b = a.copy()

for v in a:
    print(id(v))

for v in b:
    print(id(v))

똑같은 id가 나오는 것을 확인할 수 있을 것이다. 왜 이렇게 된걸까?

 

얕은 복사와 깊은 복사란 것이 있다. 얕은 복사는 그냥 겉핥기 식으로 복사한다면, 깊은 복사는 참조할 수 있는 것을 보면 끝까지 파고들어서 모든 것을 싹 다 복사한다.

위의 예제 코드를 보자. 리스트 a의 크기는 얼마인가? 3이다. 리스트 3개가 들어있기 때문에 3이다. 그리고 리스트 a의 항목이 참조하고 있는 값을 그냥 갖다 쓴다. 코드로 대충 표현하자면 이렇게 될 것이다.

b = []
for v in a:
    b.append(v)

이러면 리스트 b에는 리스트 a가 가리키고 있던 각 리스트의 위치가 들어가게 될 것이고, a[0] 리스트가 수정되면 b[0]도 똑같은 리스트를 바라보고 있으므로, 값이 변한 것처럼 느껴지게 될 것이다.

만약 리스트 a와 리스트 b에 들어있는 리스트도 따로 관리하고 싶다면? 그러면 그 안에 있는 리스트도 새로 만들어야 할 것이다. 이런 동작을 수행해 주는 것이 deepcopy, 깊은 복사이다.

깊은 복사를 수행하고 싶다면 이렇게 하면 된다.

import copy

a = [[1,2,3],[4,5,6],[7,8,9]]
b = copy.deepcopy(a)

copy 모듈의 deepcopy 함수를 사용하면 된다. deepcopy의 인자로 복사하고자 하는 객체를 전달하고, 함수의 결과로 복사된 객체를 받아들이면 된다. 그러면 이제 a[0]b[0]은 다른 리스트를 참고하게 될 것이다.

a[0][1] = 99
print(b[0][1])
2

 

이 외에도 내장 모듈은 수많은 기능을 제공한다. Python 프로그래밍 중 혹시 내가 필요한 기능이 없나? 라는 생각이 들면 일단 구글링을 해 보라. 내장 모듈에서 지원하는 기능이 들어있을 수도 있다.
아니면 Python에서 제공하는 내장 모듈 목록을 확인해도 된다. 내장 모듈이 아니더라도, 남이 작성한 모듈도 사용할 일이 꽤 있을 것이다.

 

Categories:

Updated:

Comments