2013년 10월 13일 일요일

Python - module과 scoping rule

module scoping rule

 module들은 규모가 큰 Python project를 구성하는데 사용된다. Python standard library는 관리를 위해 module들로 나누어져 있다. 당신의 code를 굳이 module로 구성할 필요는 없지만, 상당히 긴 program을 작성하거나 재사용하려고 하는 code를 작성한다면 module로 구성해야 할 것이다.

module이란 무엇인가?
 module code를 담고 있는 file이다. module Python 함수(function)들이나 다른 object들의 group을 정의하며 module의 이름은 해당 file의 이름이 된다.
 module들은 거의 대부분이 Python source code를 포함하지만 compile C C++ object file들이 될 수도 있다. compile module들과 Python source module들은 동일한 방식으로 사용된다.
 연관된 Python object들을 grouping하는 것과 마찬가지로 module들은 이름 충돌 문제들을 피할 수 있게 해준다. 예를 들어 당신의 program을 위해서 mymodule이라는 module을 작성하였으며 이는 reverse라는 함수(function)을 정의하고 있다고 가장하자. 또한 당신은 동일한 program 내에서 다른 누군가의 othermodule이라는 module을 당신이 사용하려고 한다. othermodule은 당신의 reverse 함수(function)과는 다른 동작을 하는 reverse라는 함수(function)을 정의하고 있다. module이 없는 language라면 동일하게 reverse라는 이름을 가진 두 개의 다른 함수(function)들을 사용하는 것은 불가능할 것이다.  Python에서는 간단하게 당신의 main program에서 mymodule.reverse othermodule.reverse로 참조할 수 있다.
 이는 Python namespaces를 사용하기 때문에 가능하다. namespace block, 함수(function), class, module 등의 구분자들의 기본적인 dictionary이다. 이 글의 마지막에 namespaces에 대해서 더욱 상세하게 설명하겠지만 각각의 module은 각자의 namespace를 가지며 이는 naming 충돌을 방지해준다는 것을 알아두는 것이 좋다.
 module Python 자체를 더욱 관리가 용이하게 하는데도 사용된다. 대부분의 표준 Python 함수(function)들은 language core 내에 build 되지 않고 필요 시에 load될 수 있도록 특정 module들을 통해 제공된다.

최초 module
 module들에 대해서 알아보는 최선의 방법은 직접 생성해보는 것이므로 하나의 module을 작성할 것이다.
 mymath.py라는 text file을 생성하고 아래의 Python code text file 내에 입력한다.

File mymath.py
"""mymath - 예제 math module"""
pi = 3.14159
def area(r):
    """area(r): 반지름 r 전달받아 원의 면적을 반환"""
    global pi
    return(pi * r * r)


 Python을 실행할 수 있는 directory 내에 위의 file을 저장한다. code는 단지 pi에 값을 할당하고 함수(function)을 정의한 것이다. 모든 Python code file 이름에 .py 확장자를 붙이는 것을 강력하게 추천한다. 이는 해당 file Python source code로 구성되었다고 Python interpreter에게 알려준다. Python module은 함수(function)와 마찬가지로 첫 line document 문자열을 넣을 수 있는 option을 가지고 있다.
 자 이제 Python Shell을 시작하여 다음과 같이 typing하라.

>>> pi
Traceback (innermost last):
  File "<stdin>", line 1, in ?
NameError: name 'pi' is not defined
>>> area(2)
Traceback (innermost last):
  File "<stdin>", line 1, in ?
NameError: name 'area' is not defined
>>> 

 , Python은 상수(constant) pi나 함수(function) area를 내장하고 있지 않다.
 그럼 다음과 같이 typing해보자.

>>> import mymath
>>> pi
Traceback (innermost call last):
  File "<stdin>", line 1, in ?
NameError: name 'pi' is not defined
>>> mymath.pi
3.1415899999999999
>>> mymath.area(2)
12.56636
>>> mymath.__doc__
'mymath - 예제 math module'
>>> mymath.area.__doc__
'area(r): 반지름 r을 전달받아 원의 면적을 반환'

 위의 예제에서는 import 구문을 사용하여 mymath.py file에서 pi area 정의를 가져왔다. import 구문은 mymath라는 module을 정의하는 file을 찾으면 .py 확장자를 자동으로 추가한다. 하지만 신규 definition은 직접적으로 접근할 수 없다. py area(2) 만 입력하면 error를 발생시킨다. 대신에 이들을 포함하고 있는 module의 이름을 .으로 구분하여 pi area의 접미사로 사용하면 접근이 가능하다. 이는 name safety를 보장한다. (저자가 3.14 3.14159265에 대해서) pi를 정의하고 있는 다른 module도 있을 수 있지만 염려할 필요는 없다. module import된다고 하더라도 othermodulename.pi로 이 module pi에 접속해야 한다. 이는 mymath.pi와는 완전히 다르게 구분된다. 이런 접근 형식은 종종 자격 조건으로 참조된다. , 변수 pi module mymath에 의해서 자격을 부여받는다. 또는 pi mymath attribute로 참조할 수도 있다.
 module 내의 정의에서는 module 이름을 앞에 붙이지 않아도 해당 module 내의 다른 정의에 접근이 가능하다. mymath.area 함수(function)은 상수 mymath.pi를 단지 pi로 해서 접근할 수 있다.
 원한다면 다음과 같이 해당 module의 이름을 접미사로 사용하지 않고 바로 정의를 이름으로 사용할 수 있게 할 수도 있다.

>>> from mymath import pi
>>> pi
3.1415899999999999
>>> area(2)
Traceback (innermost last):
  File "<stdin>", line 1, in <module>
NameError: name 'area' is not defined

 위에서 from module import name을 사용하여 명확하게 request한 후에 pi로 바로 접속이 가능함을 알 수 있다.
 함수(function) area는 명확하게 import하지 않았으므로 여전히 mymath.area로 호출되어야 한다.
 당신은 기본적인 대화형 mode IDLE Python shell에서 당신의 module을 수정하고 바로 test하려고 할 수 있다. 하지만 disk 상에 있는 당신의 module을 변경하고 import 명령어를 재입력하더라도 다시 load 되지는 않는다. 이를 위해서는 imp module reload 함수(function)을 사용해야 한다. imp module module들을 import하는 뒷단 mechanism들의 interface를 제공한다.

>>> import mymath, imp
>>> imp.reload(mymath)
<module 'mymath' from '/home/doc/quickpythonbook/code/mymath.py'>

 module이 처음으로 import 되거나 reload되면 해당 module의 전체 code parse 된다. 만약 error가 발견되면 syntax exception을 발생시킨다. 반면에 모든 것이 괜찮다면 Python byte code를 포함하고 있는 mymath.pyc(filename.pyc)가 생성된다.
 module reload하면 신규 session을 시작하여 해당 module을 처음으로 import 했을 때와 정확하게 동일한 상황이 되는 것은 아니다. 하지만 여기에 존재하는 차이점들이 일반적으로 문제를 일으키지는 않는다. 흥미가 있다면 Python Language Reference imp module reload section에서 상세 내용을 찾을 수 있을 것이다.
 물론 module들이 대화형 Python shell에서 사용되어야 하는 것은 아니다. module들은 script들에서 import 시킬 수도 있고, 또는 program file의 시작 부분에 적합한 import 구문들을 사용하여 import할 수도 있다. Python 내부적으로는 대화형 session script module들로 취급한다.
 지금까지의 내용을 요약하면 다음과 같다.

<![if !supportLists]> <![endif]>module Python object들을 정의하는 file이다.
<![if !supportLists]> <![endif]>만약 특정 module file의 이름이 modulename.py라면 module Python 이름은 modulename이다.
<![if !supportLists]> <![endif]>modulename이라는 이름을 가진 module import modulename 구문을 사용하여 가져올 수 있다. 이 구문이 실행된 이후에 해당 module 내에 정의된 object들에 modulename.objectname 형식을 사용하여 접근할 수 있다.
<![if !supportLists]> <![endif]>module에서 특정한 이름들을 from modulename import objectname 구문을 사용하여 program 내에서 직접적으로 가져올 수 있다. 이는 modulename을 접두사로 사용하지 않고 objectname만으로 접근할 수 있게 해준다. 또한 자주 사용되는 name들을 가져올 때 유용하게 사용될 수 있다.

import 구문
 import 구문은 세 가지 다른 형식을 가지고 있다. 가장 기본적인 형식은 다음과 같다.

import modulename

 이는 주어진 이름의 Python module을 검색하여 해당 contents parse하고 사용할 수 있게 만들어 준다. 위의 import code는 해당 module contents를 사용할 수 있게 해주지만 code 내에서 import module 내의 name 참조는 반드시 module name을 접두사로 사용해야 한다. 만약 지정된 이름의 module을 찾지 못한다면 error가 발생될 것이다. Python이 정확하게 어디에서 module들을 찾는 지에 대해서는 추후 간단하게 다룰 예정이다.
 두 번째 형태는 module에서 특정한 name들이 바로 code import 되도록 해준다.

from modulename import name1, name2, name3, . . .

 import code에서 modulename 내의 name1, name2, name3 등을 각각 사용할 수 있게 해준다. import 구문 후의 code에서는 module name접두어로 사용하지 않고도 name1, name2, name3 등을 사용할 수 있다.
 마지막으로 from ... import ... 구문의 일반적인 형식이 존재한다.

from modulename import *

 * modulename 내에서 전달되는 모든 name들을 의미한다. 이는 modulename에서 모든 public name들을 import 한다. , 밑줄로 시작되지 않는 name들에 대해서 module name접두어로 사용하지 않고 code에서 바로 name으로 사용할 수 있게 해준다. 하지만 module 이나 package __init__.py 내에 __all__ 이라는 name들의 list가 존재한다면 밑줄로 시작하는 것에 관계없이 해당 list 내의 name들은 import 되게 된다.
 특정 형식으로 import 하는 형식을 사용 시에는 반드시 주의를 해야 한다. 만약 두 개의 module들에서 동일한 하나의 name이 정의되어 있다고 가정하자. 여기에서 “from ... import ...” 형식을 사용하여 두 module들을 모두 import 한다면 name clash를 일으키며 두 번째 module name이 첫 번째 module name을 대체하게 될 것이다. 이는 또한 다른 사람들이 당신의 code를 볼 때 사용된 name들을 어디에서 가져왔는지를 알아보는 것에 어려움을 주게 될 것이다. 만약 당신이 위의 두 가지 import 구문 형식 중에 하나를 사용한다면 code를 읽는 사람들에게 이들이 어디에서 import 되는지에 대해서 명확한 정보를 주는 것이 좋다.
 그러나 나중에 살펴보게 될 tkinter와 같은 몇몇 module들은 이들을 명확하게 어디에서 import하였는지 밝히고 name clash가 발생하지 안도록 함수(function)들의 이름을 정하고 있다. 대화형 shell에서 사용하기 위해 일반적인 import keystroke로 저장하는 것도 일반적으로 사용된다.

module 검색 경로
 정확하게 Python module들을 어디에서 찾는지는 sys라는 module을 통해 접근할 수 있는 path라는 변수 내에 정의된다. 다음을 입력해보자.

>>> import sys
>>> sys.path
_list of directories in the search path_

 ‘_list of directories in the search path_’에는 당신의 system 설정에 따라서 다른 값이 보여질 것이다. 세부 내용에 상관없이 문자열은 import 구문을 실행하려고 할 때 Python에서 검색하는 순서에 따른 directory들의 list를 나타낸다. import 요청에 사용된 module들은 sys.path에 나와있는 directory list 내에서 일치하는 것이 있는지 검색한다. module 검색 경로 내에 일치하는 module이 없다면 ImportError exception을 발생시킨다.
 만약 IDLE을 사용한다면 Python Shell window File menu에서 Path Browser window를 사용하여 path module들을 시각적으로 볼 수 있다.
 sys.path 변수는 environment (operating system) 변수 PYTHONPATH가 존재한다면 이 값에서 초기화되며, 이 변수가 존재하지 않는다면 당신의 installation에 따른 기본 값에서 초기화된다. Python script를 실행할 때마다 해당 script sys.path 변수는 첫 번째 항목으로 삽입된 해당 script를 포함하는 directory를 가진다. 이는 Python program 실행을 하는 경로를 결정하는데 편리한 방식을 제공한다. 위의 예제와 같은 대화형 session 내에서 sys.path의 첫 번째 항목은 빈 문자열로 지정된다. 이는 Python에서 처음으로는 해당 module을 현재 directory 내에서 찾아야 한다는 의미로 전달된다.

당신의 module들을 어디에 위치시킬 것인가?
 이 글에서 처음으로 나온 예제인 mymath module의 실행과정을 알아보자. 먼저 대화형으로 Python을 실행하여 sys.path를 출력했을 때 첫 번째 항목이 ''이므로 Python은 현재 directory에서 module들을 검색하고 mymath.py file을 포함하는 directory 내에서 Python이 실행된다. production 환경에서는 일반적으로 이런 조건들이 사용되지는 않을 것이다. production 환경에서 당신은 Python을 대화형으로 수행하지 않을 것이며 Python code file들을 현재 directory 내에 위치시키지도 않을 것이다. 당신이 coding module들을 당신의 program들에서 사용할 수 있게 하려면 다음 사항들 중에 한 가지를 수행해야 한다.

<![if !supportLists]> <![endif]>당신의 module들을 Python에서 일반적으로 module들을 검색하는 directory 중에 하나에 위치시킨다.
<![if !supportLists]> <![endif]>Python program에서 사용되는 모든 module들을 해당 program과 동일한 directory로 위치시킨다.
<![if !supportLists]> <![endif]>당신의 module들을 위치시킬 directory 또는 directory들을 생성하고 신규 생성된 directory를 포함하도록 sys.path 변수를 수정한다.

 위의 세 가지 option들에서 첫 번째 option이 가장 쉬워보이지만 당신이 사용하는 Python version에서 기본 module 검색 경로에 local code directory들을 포함하고 있지 않다면 선택해서는 않되는 option이기도 하다. 이런 directory들은 해당 site에 특화된 code를 담기 위한 것이며 Python 설치에 관련된 part가 아니므로 신규 Python 설치에 의해서 덮어쓸 위험성은 없다. 당신의 sys.path가 이와 같은 directory들을 참조하고 있다면 당신의 module들을 이 directory들에 넣어 사용이 가능하다.
 두 번째 option은 특정 program에 관련된 module들에 대해서 좋은 선택이 될 수 있다.
 세 번째 option은 하나 이상의 program에서 사용되며 site에 특화된 module들에는 좋은 선택이 될 수 있다. sys.path를 다양한 방식으로 수정할 수 있다. 당신의 code 내에 이를 할당할 수 있다. 이는 간단한 방식이지만 당신의 program code 내에 directory 위치들을 hard-code하게 된다. 상대적으로 간단한 방식인 PYTHONPATH 환경 변수를 지정할 수도 있다. 하지만 이는 당신의 site의 모든 사용자들에게 적용되지는 않을 것이다. 또는 a.pth file을 사용하여 기본 검색 경로를 추가할 수동 있다.
 PYTHONPATH를 지정하는 것에 대해서는 환경 변수들에 대한 Python documentation(http://docs.python.org/3.3/using/cmdline.html?highlight=pythonpath#envvar-PYTHONPATH)를 참조하라. 당신은 directory directory들을 sys.path 변수에 접두어로 수식하여 지정할 수 있다. 이를 사용하면 기존에 사용하고 있는 library module들과 동일한 name을 정의할 수 없다는 것을 주의해야 한다. 당신의 module은 해당 library module보다 더 앞서서 인식될 것이다. 몇몇 경우에 이는 당신이 앞에서 예기한 방식을 의도할 수도 있겠지만 대부분에 있어서 이를 의도하지는 않을 것이다.
 이러한 문제는 .pth 방식을 사용하여 방지할 수 있다. 이를 이용하면 당신이 추가하려는 directory directory들을 sys.path에 추가할 수 있다. 간단한 예제를 통해 이 mechanism을 설명하도록 하겠다. Linux에서 Python3 package를 설치한 경우에는 당신은 이 file /usr/local/lib/python3.3/site-packages directory에 위치시킬 수 있다. 당신의 /usr/local/lib/python3.3/site-packages이라고 가정하고 다음의 file /usr/local/lib/python3.3/site-packages에 위치시킨다.

File myModules.pth
mymodules
/home/python/modules


 이 다음에 Python interpreter가 시작되고 /usr/local/lib/python3.3/site-packages/mymodules /home/python/modules 가 존재한다면 sys.path에 이들을 추가하게 될 것이다. 이 후에는 해당 directory들 내에 당신의 module들을 위치시킬 수 있다. 그러나 여전히 mymodules directory는 신규 설치에서 덮어써질 수 있는 위험을 안고 있다는 것에 주의하라. modules directory는 더 안전한 상태이다. Python upgrade 한다면 myModules.pth file을 이전하거나 생성해야 할 수도 있다. .pth file들의 사용에 대한 상세 사항을 확인하려고 한다면 Python Library Reference 내에서 site module 설명(http://docs.python.org/3.3/library/site.html)를 참조하라.

module들 내의 private name
 위에서 from module import *를 입력하여 module에서 거의 모든 name들을 import할 수 있다고 언급하였다. 여기에서 예외 사항으로 module 내에서 밑줄(_)로 시작되는 name들은 앞의 방식으로는 import 될 수 없으므로 사람들은 from module import * import를하는 module들을 작성할 수 있다. 당신은 (해당 module의 외부에서 접근하지 못하는) 밑줄(_)로 시작하는 내부 name들을 사용하여 사용자가 접근하려고 하는 name들만 from module import *로 가져올 수 있게 해줄 수 있다.
 실제로 동작하는 것을 볼 수 있도록 아래의 code를 포함하고 있는 modtest.py라는 file을 가지고 있다고 해보자.

File modtest.py
"""modtest: our test module"""

def f(x):
    return x

def _g(x):
    return x

a = 4
_b = 2


 이제 대화형 session을 시작하여 다음을 입력한다.

>>> from modtest import *
>>> f(3)
3
>>> _g(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_g' is not defined
>>> a
4
>>> _b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_b' is not defined

 위에서 보이는 것처럼 name f a import 되었지만 _g _b modtest의 외부에서 숨겨진 채로 남아있다. 이 현상은 from ... import *에서만 발생한다는 것에 주의하라. _g _b는 다음과 같이 접근할 수 있다.

>>> import modtest
>>> modtest._b
2
>>> from modtest import _g
>>> _g(5)
5

 private name들을 나타내기 위한 밑줄(_)로 시작하는 방식은 module들에서만 사용되는 것이 아니라 Python 전체를 통해서도 사용된다. 추후에 class들과 package들에서 이를 보게 될 것이다.

library third-party module
 이 글의 앞 부분에서 표준 Python distribution module들로 나눠져 있어서 관리가 용이하게 되어있다고 언급하였다. Python을 설치하면 library module들 내의 모든 기능들을 바로 사용할 수 있다. 사용 전에 필요한 모든 적절한 module, 함수(function), class, 등등을 명확하게 import 해줘야 한다.
 앞으로 매우 일반적이고 유용한 standard module들의 대다수를 다룰 예정이다. 하지만 표준 Python distribution은 다루려고 하는 것보다 더 많은 것들을 포함하고 있다. 최소한 Python Library Reference table of contents를 훑어보는 것이 좋은 것이다.
 IDLE에서는 Path Browser window를 사용하여 Python으로 작성된 것들을 쉽게 browse 하고 살펴볼 수 있다. 또한 Python Shell window Edit menu에서 찾을 수 있는 Find in Files 대화 box에서 예제 code를 사용하고 있는 것들도 검색할 수 있다. 이 방식으로 당신의 module들도 마찬가지로 검색이 가능하다.
 사용 가능한 third-party module들과 이들의 link들은 Python home page에서 구분되어져 있다. 이들을 당신의 program들에서 import하여 사용하려면 download하여 당신의 module 검색 경로에 있는 directory에 위치시켜야 한다.

Python scoping rule들과 namespace
 Python scoping rule들과 namespace들은 당신이 Python programmer로 성장을 경험하면서 더욱 관심을 가지게 될 것이다. 만약 당신이 Python을 새롭게 배운다면 기본 지식을 얻기 위해서 이 text를 속독하는 것 이상을 할 필요는 없을 것이다. 상세 사항은 Python Language Reference 내의 “namespaces”를 찾아보라.
 여기에서 핵심 개념은 namespace이다. Python 내에서 namespace구분자(identifier)들과 object들을 mapping 시켜주며 보통 dictionary로 표현된다. Python에서 code block이 실행될 때 local, global, built-in의 세 가지 namespace들을 가진다.
그림 구분자(identifier)가 위치하는 namespace를 점검하는 순서

 실행 중에 구분자(identifier)를 만나게 되면 Python은 먼저 local namespace에서 이를 찾는다. 만약 local namespace에서 찾지 못한다면 다음으로 global namespace에서 이를 찾는다. global namespace에서도 여전히 찾지를 못한다면 built-in namespace에서 이를 찾을 것이다. built-in namespace에서도 존재하지 않는다면 error로 간주하고 NameError exception을 발생시킬 것이다.
 module, 대화형 session에서 실행되는 명령어, file에서 수행되는 script에서 global namespace local namespace들은 동일하다. 변수(variable)이나 함수(function)을 생성하는 것이나 다른 module에서 무엇인가를 import하는 것은 해당 namespace 내에 새로운 항목으로 만들어지거나 binding이 된다.
 하지만 함수(function)가 호출될 때는 local namespace가 생성되고 해당 호출의 각 parameter 마다 binding이 들어가게 된다. 그러면 해당 함수(function) 내서 변수(variable)가 생성될 때마다 local namespace로 새로 binding이 들어가게 될 것이다. 함수(function) global namespace (module, script file, 대화형 session) 함수(function)에 포함된 block global namespace이다. 이는 호출된 dynamic context와는 별개이다.
 어떤 상황이든 built-in namespace __builtins__ module의 해당된 것이다. module은 무엇보다 (len, min, max, int, float, long, list, tuple, cmp, range, str, repr과 같이) 당신이 보게 되는 모든 내장 함수(built-in function)들과 Python 내에서 NameError exception과 같은 다른 built-in class들을 포함한다. 가끔씩 새로운 Python programmer들이 난관을 겪는 점은 built-in module 내의 항목들을 덮어쓸 수 있다는 사실이다. 예를 들어 당신의 program 내에 list를 생성하여 list라고 이름을 지은 변수에 넣었다면 그 뒤부터는 built-in 함수(function) list를 사용할 수 없게 될 것이다. Python에서는 당신의 list 항목을 먼저 찾게 될 것이다. 함수(function)들과 module들의 이름과 다른 object들의 이름에는 차이가 없으므로 가장 최근에 일어난 주어진 구분자(identifier) binding이 사용되게 된다.
 충분히 설명이 된 것으로 판단하고 몇 가지 예제들을 살펴볼 것이다. 우리는 locals, globals 이 두 가지 built-in 함수(function)들을 사용해볼 것이다. 이들은 각각 local namespace global namespace들 내의 binding들을 포함하는 dictionary들을 반환한다.
 새로운 대화형 session을 시작한다.

>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, '__loader__': <class '_frozen_importlib.BuiltinImporter'>}
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, '__loader__': <class '_frozen_importlib.BuiltinImporter'>}
>>> 

 새로운 대화형 session에서 local namespace global namespace들은 동일하다. 이들은 내부적으로 사용되는 세 가지 inital key/value 쌍들을 가지고 있다. 째로 빈 문서 문자열 __doc__, 째로 (대화형 session, file들에서 실행되는 script들에서의  언제나 __main__으로 명시되는) main module name __name__, 째로 built-in namespace __builtins__에 사용되는 module (__builtins__ module)으로 구성된다.
 이제 우리는 변수 생성과 module에서의 importing을 하여 생성된 다수의 binding을 볼 것이다.

>>> z = 2
>>> import math
>>> from cmath import cos
>>> globals()
{'__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'math': <module 'math' from '/usr/local/lib/python3.3/lib-dynload/math.cpython-33m.so'>, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, 'z': 2, '__doc__': None, 'cos': <built-in function cos>}
>>> locals()
{'__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'math': <module 'math' from '/usr/local/lib/python3.3/lib-dynload/math.cpython-33m.so'>, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, 'z': 2, '__doc__': None, 'cos': <built-in function cos>}
>>> math.ceil(3.4)
4

 예상대로 local namespace들과 global namespace들은 계속 동일하다. z를 숫자로 math module cmath module에서 cos를 함수(function)으로 하여 항목들이 추가되었다.
 del 구문을 사용하여 (import 구문으로 생성된 module binding들을 포함한) 신규 binding들을 namespace에서 삭제할 수 있다.

>>> del z, math, cos
>>> locals()
{'__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, '__doc__': None}
>>> math.ceil(3.4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'math' is not defined
>>> import math
>>> math.ceil(3.4)
4

 math module을 다시 import하여 사용할 수 있으므로 극단적인 결과를 가져오지는 않는다. 대화형 mode에서는 위의 방식으로 del을 편리하게 사용할 수 있다.
 좀 더 살펴보면 __doc__, __main__, __builtins__ 항목들도 del을 이용하여 삭제가 가능하다. 하지만 이런 방식을 사용하는 것은 당신의 session의 상태를 나쁘게 하므로 지향하는 것이 좋다.
 이제는 대화형 session 내에서 생성된 함수(function)을 살펴보자.

>>> def f(x):
...     print("global: ", globals())
...     print("Entry local: ", locals())
...     y = x
...     print("Exit local: ", locals())
...
>>> z = 2
>>> globals()
{'__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'math': <module 'math' from '/usr/local/lib/python3.3/lib-dynload/math.cpython-33m.so'>, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, 'f': <function f at 0x2aef4df94710>, 'z': 2, '__doc__': None}
>>> f(z)
global:  {'__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'math': <module 'math' from '/usr/local/lib/python3.3/lib-dynload/math.cpython-33m.so'>, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, 'f': <function f at 0x2aef4df94710>, 'z': 2, '__doc__': None}
Entry local:  {'x': 2}
Exit local:  {'x': 2, 'y': 2}
>>> 

  위의 예제를 분석해보자. parameter x f local namespace 내의 original 항목이지만 y는 후반에 추가된다. 대화형 session 내에서 f가 정의되었으므로 global namespace local namespace는 동일하다. f 다음에 정의된 z를 포함하는 것에 주목하라.
 production 환경에서 당신은 일반적으로 module들 내에서 정의된 함수(function)들을 호출하게 될 것이다. 이 함수(function)들의 global namespace는 이들이 정의된 module이다. 다음 file을 생성했다고 가정해보자.

File scopetest.py
"""scopetest: our scope test module"""
v = 6

def f(x):
    """f: scope test function"""
    print("global: ", list(globals().keys()))
    print("entry local: ", locals())
    y = x
    w = v
    print("exit local: ", list(locals().keys()))


 우리는 globals에 의해서 반환되는 dictionary key(identifier)들만 출력하려고 한다. 이는 결과값들을 잘라서 줄여줄 것이다. module들 내에서 최적화를 위해 전체 __builtins__ dictionary __builtins__ key value field 내에 저장되는 점 때문에 위와 같은 예제를 작성한 것이다.

>>> import scopetest
>>> z = 2
>>> scopetest.f(z)
global:  ['f', '__name__', '__initializing__', '__package__', '__builtins__', '__doc__', '__cached__', 'v', '__file__', '__loader__']
entry local:  {'x': 2}
exit local:  ['y', 'x', 'w']

 global namespace는 이제 scopetest module이며 함수(function) f와 정수(integer) v를 포함한다. (위의 대화형 session에서 z는 포함되지 않는다.) 이와 같이 module을 생성하면 당신은 이들 함수(function)들의 namespace들에 대해서 완전한 제어권을 가지게 된다.
 위에서 local namespace global namespace를 예제로 알아보았으며 다음으로는 built-in namespace에 대해서 예제를 통해 알아보자. 여기에서 주어진 module 내에 정의된 name들의 list를 반환하는 또 다른 내장 함수(built-in function) dir을 소개할 것이다.

>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

 __builtins__ module에는 많은 항목들이 존재한다. Error System Exit로 끝나는 name들은 Python에 내장된 exception들의 name들이다. 추후 exception들에 대해서도 알아볼 예정이다.
 마지막 group (abs에서 zip까지) Python의 내장 함수(built-in function)들이다. 이들 중에 다수는 당신이 이전 글에서 본 적이 있는 것들도 있을 것이고 앞으로 보게 될 것들도 있다. 하지만 모든 것들을 다루지는 못 할 것이다. 만약 여기에 관심이 있다면 Python Library Reference에서 나머지 상세 사항들을 볼 수 있을 것이다. 또는 help() 함수(function)을 사용하거나 docstring을 바로 출력하여 언제든지 이들의 documentation string을 볼 수도 있다.

>>> print(max.__doc__)
max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value

With a single iterable argument, return its largest item.
With two or more arguments, return the largest argument.
>>> 

 앞에서 언급되었던 것처럼 부주의하게 내장 함수(built-in function)을 덮어쓸 수 있다는 것을 신규 Python programmer는 들어보지 못했을 것이다.

>>> list("Peyto Lake")
['P', 'e', 'y', 't', 'o', ' ', 'L', 'a', 'k', 'e']
>>> list = [1, 3, 5, 7]
>>> list("Peyto Lake")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not callable

 Python interpreter는 우리가 내장 함수(built-in function) list에서 새로운 list로의 binding을 감지하지 못하며, 내장 함수(built-in function) list의 문법을 사용하더라도 신규 binding으로 인식한다.
 우리가 하나의 namespace에 동일한 구분자(identifier)두번 사용하려고 할 때도 당연히 동일하게 동작한다. 기존의 값은 type에 상관없이 덮어써질 것이다.

>>> import mymath
>>> mymath = mymath.area
>>> mymath.pi
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'pi'

 당신이 이것을 후에 인지하였다고 해도 이는 심각한 문제가 되지는 않는다. 하지만 다른 type object들이라도 구분자(identifier)들을 재사용하는 것은 가독성이 높은 code에 기여하지는 않는다. 만약 당신이 대화형 mode에서 부주의하게 실수로 구분자(identifier)를 덮어써도 쉽게 복구가 가능하다. 덮어쓴 내장 함수(built-in function) del을 사용하여 당신이 만든 binding을 제거하여 재접근을 할 수 있으며, 당신의 module은 다시 import하여 재접근이 가능하다.

>>> del list
>>> list("Peyto Lake")
['P', 'e', 'y', 't', 'o', ' ', 'L', 'a', 'k', 'e']
>>> import mymath
>>> mymath.pi
3.14159

 locals globals 함수(function)들은 단순한 debugging 도구로 유용하게 사용할 수 있다. dir 함수(function)은 현재 setting들을 주지는 않지만, parater들이 없이 호출하면 local namespace 내에 구분자(identifier)들의 정렬된 list를 반환해준다. 이는 type이 잘못 정해진 변수(variable) error를 잡는데 도움이 된다.

>>> x1 = 6
>>> xl = x1 - 2
>>> x1
6
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'x1', 'xl']

 IDLE bundle로 포함된 debugger는 당신의 code에서 local global 변수(variable) setting들을 볼 수 있도록 해주는 setting들을 가지고 있다. 이는 locals globals 함수(function)들의 출력을 표시한다.

요약
 Python Python code object들의 관리를 위해 module들을 사용하기도 하지만 당신이 연관 code object들을 file에 넣을 수 있도록 module을 사용하기도 한다. 이는 많은 양의 code를 쉽게 관리하고 재사용할 수 있게 해준다. 뿐만 아니라 module부터 import 된 각각의 object는 일반적으로 해당 module name을 같이 사용하므로 변수(variable) 이름들의 충돌을 방지하는데도 도움을 준다.
 다음으로 Python으로 standalone program들과 script들을 작성하는데 필요한 지식의 마지막 조각인 연관 함수(function)들을 module들로 packaging하는 것을 알아볼 것이다.