모듈

- 여러 코드를 한데 묶어 다른 곳에서 재사용 할 수 있는 코드 모음

- 보통 비슷한 기능을 하는 함수나 큰 기능을 수행 하는데 필요한 일련의 함수와 데이터가 포함


모듈 사용하기

- import 명령은 내장 모듈을 현재 이름공간으로 가져옴

 

1. math모듈

- 수학과 관련된 작업을 처리해야 할 때 사용

- dir() 함수를 사용하면 모듈에 어떠한 함수, 데이터가 정의 되있는지 알 수 있음

import math


print("math.pow(2, 10):", math.pow(2, 10))

print("math.log(100):", math.log(100))

print("math.pi:", math.pi)

print("dir(math):", dir(math))


math.pow(2, 10): 1024.0

math.log(100): 4.605170185988092

math.pi: 3.141592653589793

dir(math): ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']


2. FTP

- 예제 생략


3. 그 밖의 모듈

- 문자열, 날짜, 시간, 수학, 분수, 십진법, 랜덤, 파일, sqllite3, os, sys, threading, unites, xml, email, http

- 200여개의 모듈이 존재

- 표준 라이브러리에 대한 설명: https://docs.python.org/3/library/index.html 참조


4. 모듈을 사용하는 이유

1) 코드의 재사용

- 자주 사용되는 기능을 모듈로 구현하여 사용하면 반복적으로 사용할 필요가 없음

- 다음 프로그램 구현에도 모듈을 추가하여 재사용할 수 있음

2) 코드를 이름 공간으로 구분하고 관리

- 모듈은 기본적으로 자기 자신을 이름으로 하는 이름 공간을 가짐

- 한정: 모듈을 import 하면 이름공간이 생성 되고 모듈명.attibute_name과 같은 형식으로 모듈의 함수나 어트리뷰트(데이터)를 사용 할 수 있음

- 함수나 데이터가 이름이 똑같아서 충돌하는 것을 방지 할 수 있음


모듈 만들기

- 모듈을 만든다는 것 =  파일을 만든다는 것, 모듈이 파일로 저장 되기 때문에..

1) 모듈:  simpleset.py


from functools import*


def intersect(*ar):

    "교집합"

    return reduce(__intersectSC, ar)


def __intersectSC(listX, listY):

    setList = []

    for x in listX:

        if x in listY:

            setList.append(x)

    return setList


def difference(*ar):

    "차집합"

    setList = []

    intersectSet = intersect(*ar)

    unionSet = union(*ar)

    

    for x in unionSet:

        if not x in intersectSet:

            setList.append(x)

    return setList


def union(*ar):

    "합집합"

    setList = []

    for item in ar:

        for x in item:

            if not x in setList:

                setList.append(x)

    return setList


2) 모듈 동작 테스트

- 모듈이 제대로 import되었는지 dir()로 확인

import simpleset

print("dir(simpleset):", dir(simpleset))


dir(simpleset): ['WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', '__builtins__', '__cached__', '__doc__', '__file__', '__intersectSC', '__loader__', '__name__', '__package__', '__spec__', 'cmp_to_key', 'difference', 'intersect', 'lru_cache', 'partial', 'partialmethod', 'reduce', 'singledispatch', 'total_ordering', 'union', 'update_wrapper', 'wraps']

setA = [1, 3, 7, 10]

setB = [2, 3, 4, 9]


print("setA:", setA, "setB:", setB)

setA: [1, 3, 7, 10] setB: [2, 3, 4, 9]


print("simpleset.union(setA, setB):", simpleset.union(setA, setB))

simpleset.union(setA, setB): [1, 3, 7, 10, 2, 4, 9]


print("simpleset.intersect(setA, setB, [1, 2, 3]):", 

      simpleset.intersect(setA, setB, [1, 2, 3]))

simpleset.intersect(setA, setB, [1, 2, 3]): [3]



모듈의 경로

- 이름만으로 임포트 되게하려면 모듈 검색 경로에 모듈 파일이 있어야 함

- 프로그램이 실행된 홈 디렉토리 -> PYTHONPATH 환경변수 -> 표준 라이브러리 디렉터리 순서로 모듈을 찾음


1. 프로그램이 실행된 홈 디렉토리

2. PYTHINPATH에 등록

1) .bash_profile에 path 추가

2) eclipse에 path 추가


3. 표준 라이브러리 디렉터리

1) sys.path

-  모듈 검색 경로 목록을 리스트 형식으로 관리

import sys

print("sys.path:", sys.path)

sys.path: ['/Users/eunguru/Source/Python/Module/src', '/Users/eunguru/Source/Python/Module/src', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip']

2) sys.path에 경로를 동적으로 추가/삭제 (테스트를 위해 /Users/eunguru/Source/Test 추가/삭제)

sys.path.append("/Users/eunguru/Source/Test")

print("sys.path:", sys.path)

sys.path: ['/Users/eunguru/Source/Python/Module/src', '/Users/eunguru/Source/Python/Module/src', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip', '/Users/eunguru/Source/Test']

sys.path.remove("/Users/eunguru/Source/Test")

print("sys.path:", sys.path)

sys.path: ['/Users/eunguru/Source/Python/Module/src', '/Users/eunguru/Source/Python/Module/src', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip']


모듈 임포트

- 파이썬에서는 import 구문을 어디서나(함수나 제어문 안에서도) 사용 가능

def loadMathMod():

    print("import math")

    import math

    print("dir(math):", dir(math))

loadMathMod()

import math

dir(math): ['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']


1. 모듈 임포트

1) import 모듈이름

2) from <모듈> import <어트리뷰트>

- <모듈>안에 정의된 어트리뷰트 중 이름에 해당하는 어트리뷰트를 현재의 이름공간 안으로 임포트

- 임포트된 어트리뷰트는 모듈이름.어트리뷰트이름 같은 형식으로 쓰지 않고 바로 참조 가능

from simpleset import union


print("union([1, 2, 3], [3], [3, 4]):", union([1, 2, 3], [3], [3, 4]))

union([1, 2, 3], [3], [3, 4]): [1, 2, 3, 4]


print("intersect([1, 2], [1]):", intersect([1, 2], [1]))

Traceback (most recent call last):

  File "/Users/eunguru/Source/Python/Module/src/Module.py", line 39, in <module>

    print("intersect([1, 2], [1]):", intersect([1, 2], [1]))

NameError: name 'intersect' is not defined

- 만약 새로 임포트 되는 이름이 이미 이름공간에 있는 이름인 경우, 기존 이름들이 새로 임포트된 이름들로 대체됨

기존 함수가 사라질수 있으므로 주의!

def union(a):

    print(a)


print("union:", union)

union: <function union at 0x1018aa048>


union("test")

test


# simpleset 모듈에서 union 이름을 가진 함수를 임포트 

from simpleset import union

# 주소가 변경

print("union:", union)

union: <function union at 0x1018a1f28>


# simpleset 모듈의 함수가 실행 

print("union(\"test\"):", union("test"))

union("test"): ['t', 'e', 's']

3) from <모듈> import *

- 모듈내 이름 중 밑줄(_)로 시작하는 어트리뷰트(데이터, 함수)를 제외하고 모든 어트리뷰트를 현재의 이름공간으로 임포트

- 모듈 내부에서만 쓰이고 외부에서는 안쓰이는 함수나 데이터일 경우 이름을 밑줄로 시작하면 코드 충돌 확률 감소, 이름공간에 모듈내의 함수가 쓸데없이 임포트 되는 것을 방지

import simpleset

print("dir(simpleset):", dir(simpleset))

dir(simpleset): ['WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', '__builtins__', '__cached__', '__doc__', '__file__', '__intersectSC', '__loader__', '__name__', '__package__', '__spec__', 'cmp_to_key', 'difference', 'intersect', 'lru_cache', 'partial', 'partialmethod', 'reduce', 'singledispatch', 'total_ordering', 'union', 'update_wrapper', 'wraps']

from simpleset import *

print("dir():", dir())

dir(): ['WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'cmp_to_key', 'difference', 'intersect', 'loadMathMod', 'lru_cache', 'math', 'partial', 'partialmethod', 'reduce', 'singledispatch', 'sys', 'total_ordering', 'union', 'update_wrapper', 'wraps']


4) import <모듈> as <별칭>

- 모듈 이름을 별칭으로 변경하여 임포트

- 모듈 이름이 길거나 모듈을 다른 이름으로 참조하고자 할 때 사용

import xml.sax.handler as handler

print("handler:", handler)

handler: <module 'xml.sax.handler' from '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/xml/sax/handler.py'>



모듈 임포트 파헤치기

1. 모듈 임포트시 모듈 실행 단계

- sys.path에 등록된 디렉터리에서 모듈을 찾음

- 모듈의 바이트 코드가 있으면 바로 임포트

- 모듈의 바이트 코드가 없으면 인터프리터를 이용해 해당 모듈의 코드를 바이트 코드를 만든 후 모듈 파일이 존재하는 디렉터리에 저장

(예: 모듈 simpleset.py 바이트 코드 simpleset.pyc)

- 바이트 코드는 모듈이 임포트 될때 인터프리팅 되지 않고 바로 메모리에 로딩, 임포트가 더 빨라짐

- 바이트 코드는 모듈의 내용이 변경되지 않는 이상 재생성 되지 않음

- 모듈이 인포트 되면 어트리뷰트들을 현재의 이름 공간으로 가져오거나 모듈의 이름을 현재 이름 공간으로 가져옴

- 코드를 메모리에 로딩한 후 모듈의 코드가 실행

1) 예제

- 모듈: testmodule.py

print("test module")

defaultvalue = 1

def printDefaultValue():

    print("defaultvalue:", defaultvalue)

- 모듈 동작 테스트

import testmodule

test module


testmodule.printDefaultValue()

defaultvalue: 1

2) 예제 2

- 모듈이 임포트 되어 모듈 코드가 로딩되면 프로그램이나 파이썬 인터프리터가 끝나기 전까지 변경되지 않음

- 모듈 변경

print("hello world")

defaultvalue = 2

def printDefaultValue():

    print("defaultvalue:", defaultvalue)

- 다시 모듈 임포트

import testmodule

test module

testmodule.printDefaultValue()

defaultvalue: 1

* 해당 코드는 IDLE에서 테스트 했음, PyDev는 자동적으로 인터프리터가 다시 시작되는지 변경된 값이 출력

- IDEL 또는 파이썬 인터프리터 종료 후 재시작

import testmodule

hello world

testmodule.printDefaultValue()

defaultvalue: 2

3) 이미 임포트 된 모듈을 다시 임포트 시키는 예제

import testmodule

import imp

imp.reload(testmodule)

hello world

4) 모듈에 대한 레퍼런스 예제

- 파이썬에서는 모든것이 객체

- 모듈이 임포트 되면 코드가 메모리에 로딩되면서 레퍼런스를 전달해줌

- 모듈은 메모리에 하나만 로딩, 로딩된 모듈을 참조하는 여러개의 레퍼런스가 생성될 수 있음

import testmodule as imp1

hello world


import testmodule as imp2

print("dir():", dir())

dir(): ['WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'cmp_to_key', 'difference', 'handler', 'imp1', 'imp2', 'intersect', 'loadMathMod', 'lru_cache', 'math', 'partial', 'partialmethod', 'reduce', 'singledispatch', 'sys', 'total_ordering', 'union', 'update_wrapper', 'wraps']

imp1.printDefaultValue()

defaultvalue: 2


imp1.defaultvalue = 100

imp2.printDefaultValue()

defaultvalue: 100


'__main__'을 사용한 유용한 팀

1. 터미널에서 모듈을 직접 실행

- 모듈:  mymod.py 

print("my module")

my module


2. 임포트 되었을때와 직접 실행 했을때 다른 동작을 하게 하는 방법

- 파이썬에서는 별도 함수나 값을 제공하지 않음

- __name__ 어트리뷰트를 이용해서 구분 할 수 있음, __name__값이 달라지기 때문에 모듈이 임포트 될 때 혹은 직접 실행 되었을때 각각 다른 코드를 실행 할 수 있음

1) __name__값 확인 예제

- 수정된 모듈

print("my module")

print(__name__)

a. 직접 실행시 결과

my module

__main__

b. 임포트 시 결과

- 모듈 임포트 코드, 결과

import mymod

mymod.__name__

my module

mymod

2) __name__을 사용해 메인으로 코드가 실행 되었을 때와 모듈을 임포트 했을때 다르게 실행 되는 예제

print("my module")


if __name__ == '__main__':

    print("모듈을 직접 실행했음.")

else:

    print("모듈을 임포트 했음.")

a. 직접 실행 시 결과

my module

모듈을 직접 실행했음.

b. 임포트 시 결과

import mymod

mymod.__name__

my module

모듈을 임포트 했음.


3. __name__을 이용한 구분 방법 활용

- 모듈을 개발할 때 사용

- 모듈을 수정하거나 코드를 추가할 때마다 임포트해서 테스트 코드를 수행하는 것보다 직접 실행 했을 때 테스트를 수행하면 바로 테스트 결과 확인 가능 

- IDLE이나 파이썬 인터프리터를 종료 후 재실행 하지 않아도 됨(개발 효율성 증가)

- 예제: 모듈 testmodule2.py

defaultValue = 0

def printDefaultValue():

    print("defaultValue:", defaultValue)


if __name__ == '__main__':

    # test code

    print("hello world")

    defaultValue = 100

    printDefaultValue()

hello world

defaultValue: 100



패키지

- 여러개의 모듈을 하나로 묶어야 되는 경우가 필요

- 패키지는 모듈 이름에 "."을 붙여서 파이썬 모듈 이름공간을 구조화 하는 방법

- 패키지 디렉토리 안에는 __init_.py파일이 존재

-  __init__.py: 패키지를 초기화 하는 파일


1. 패키지 임포트 예제

xml.dom은 xml 패키지 안에 dom이라는 하위 모듈이 있음을 의미


1) xml 디렉터리에 있는 __init__.py 내용

- 20번째 구문은 모듈을 임포트 할때 어떤 모듈을 임포트 할지를 정의


2)  __all__ 구문에서 dom 모듈 삭제후 임포트 시도 예제

a. dom 삭제

b. 임포트 시도

- __all__은 import *을 실행할 때 포함할 하위 패키지 목록을 나타냄


3) 패키지 임포트

- 모듈을 임포트 할 때와 마찬가지로 패키지 임포트 시에도 __init__.py가 실행

- __version__, _MINIMUM_XMLPLUS_VERSION 변수들이 메모리에 할당 되고 값이 저장

* 책에는 예제가 나왔는데.. 실제 __init__.py에는 변수들이 없음. 확인해보겠음


2. 패키지 임포트 주의 사항

- 패키지를 임포트 했을때 해당 패키지의 하위 패키지는 자동으로 임포트 되지는 않음

import xml

print("xml:", xml)

xml: <module 'xml' from '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/xml/__init__.py'>

print("xml.dom:", xml.dom)

Traceback (most recent call last):

  File "/Users/eunguru/Source/Python/Module/src/Module.py", line 86, in <module>

    print("xml.dom:", xml.dom)

AttributeError: 'module' object has no attribute 'dom'


print("xml.dom.getDOMImplementation:", xml.dom.getDOMImplementation)

Traceback (most recent call last):

  File "/Users/eunguru/Source/Python/Module/src/Module.py", line 87, in <module>

    print("xml.dom.getDOMImplementation:", xml.dom.getDOMImplementation)

AttributeError: 'module' object has no attribute 'dom'

- xml.dom을 임포트 하도록 수정

import xml.dom

print("xml:", xml)

xml: <module 'xml' from '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/xml/__init__.py'>


print("xml.dom:", xml.dom)

xml.dom: <module 'xml.dom' from '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/xml/dom/__init__.py'>


print("xml.dom.getDOMImplementation:", xml.dom.getDOMImplementation)

xml.dom.getDOMImplementation: <function getDOMImplementation at 0x10220b158>


3. 패키지 안에서 패키지 안의 모듈 참조하기

- sound 패키지

sound/

__init__.py

formats/

__init__.py

wavread.py -> 현재 작성하는 모듈

auread.py

auwrite.py

...

effects/

__init__.py

echo.py

filters/

__init__.py

equalizer.py

1) 작성하는 모듈이 formats 하위 모듈 wavread.py 일 때,

a. auread 모듈을 임포트, 참조하는 모듈이 같은 디렉터리에 있거나, 하위 디렉터리에 있을 경우 

from auread import import *

b. equalizer 모듈을 임포트, 참조해야 하는 모듈이 상위의 다른 디렉터리에 있는 경우

from sound.filters.equalizer import equalizer


2) .을 사용해서 현재 패키지와 부모패키지 참조 

- effects 아래의 패키지에 포함된 모듈은 아래의 방법을 참조

a. 현재 디렉터리에서 echo 모듈을 임포트

from . import echo

b. 부모 디렉터리에 있는 모듈중 format 모듈을 가져옴

from .. import  formats

c. 부모 디렉터리 모듈 중 filters의 equalizer 모듈을 임포트

from ..filters import import equalizer


'컴&프로그래밍 > Python' 카테고리의 다른 글

13. 파일시스템  (0) 2015.07.02
9. C/C++와 연동  (3) 2015.03.14
8. 입출력  (0) 2015.01.14
7. 예외처리  (0) 2015.01.02
Mac OS X Yosemite에서 PyCharm 설치 후 실행  (0) 2014.12.27
5. 클래스  (0) 2014.12.20
4. 제어  (0) 2014.12.19
Google Python Tutorial - Basic Python Exercises #2 List  (0) 2014.12.11
Google Python Tutorial - Basic Python Exercises #1 String  (0) 2014.12.05