본문 바로가기
Python/파이썬 프로그래밍 연습

이름공간

by 가므자 2012. 4. 24.

들어가는

이름공간이 뭐야? 질문 소리가 들리는 듯합니다. , 이름공간은 약간 설명하기가 어렵습니다. 특별히 복잡해서가 아니라 언어마다 다르게 설명하기 때문입니다. 그 개념은 들리는 그대로입니다. 이름공간은 공간이나 지역입니다. 프로그램 안에 있으며, 그 안에서만 이름이 (변수, 클래스 등등이) 유효합니다. 실제로 이 아이디어를 일상적으로 사용합니다. 큰 회사에서 일하고 있고 삼수라는 동료가 있다고 가정해 봅시다. 회계 부서에 또다른 삼수가 있습니다. 자주는 아니지만 가끔 그를 봅니다. 그 경우 그 동료를 "삼수"라고 부르고 다른 삼수는 "회계 부서의 삼수"라고 부릅니다. 여옥이라는 동료가 있고 엔지니어링 부서에도 긴밀하게 함께 일하는 여옥이라는 동료가 있다고 생각해 봅시다. 그런 경우 각각 "우리 여옥" 그리고 "엔지니어링 부서의 여옥"이라고 지칭합니다. 부서 이름으로 수식하고 있습니다. 보이십니까? 그것이 바로 프로그램에서 이름공간이 하는 일입니다. 이름공간은 프로그래머와 번역자에게 여러 동일한 이름 중에서 어떤 이름을 가리키고 있는지 알려줍니다.

이름공간은 (BASIC 같은) 초기 프로그래밍 언어에서 전역 변수만 있었기 때문에 도입되었습니다. , 프로그램 어느 곳에서도 볼 수 있는 변수만 있었습니다 - 심지어 함수 안에 있아도 볼 수 있었습니다. 이 때문에 거대한 프로그램은 관리하기가 어려웠는데 한 곳에서 변수를 바꾸어도 다른 곳에서는 인지하지 못했기 때문입니다 - 이를 부작용(side-effect)이라고 불렀습니다. 이를 해결하기 위하여 (현대적인 BASIC들을 포함하여) 나중에 나온 언어에서는 이름공간이라는 개념을 도입하였습니다. (C++은 이를 극단적으로 취해서 프로그래머는 프로그램 어느곳이든 자신만의 이름공간을 만들 수 있습니다. 이는 라이브러리를 만드는 이들에게 유용합니다. 이들은 다른 사람들이 공급한 라이브러리와 섞일 때 자신이 만든 함수 이름들이 유일하게 유지되기를 바랍니다)

이름공간을 기술하는데 사용되는 또다른 용어는 영역(scope)입니다. 이름의 영역은 프로그램에서 그 이름이 애매모호하지 않게 사용될 수 있는 곳까지 미칩니다. 예를 들어 함수나 모듈 안이 영역입니다. 이름의 공간은 정확하게 그의 영역과 같습니다. 두 용어 사이에 아주 미묘한 차이가 몇 가지 있지만 사이비 컴퓨터 과학자나 되어야 논쟁을 벌일까 우리의 목적이라면 이름공간과 영역은 동일합니다.

파이썬의 접근법

파이썬에서 모듈마다 자신만의 이름공간을 만듭니다. 그런 이름에 접근하려면 앞에다 모듈의 이름을 놓든가 아니면 사용하고 싶은 그 이름들을 모듈 이름공간 안으로 명시적으로 반입해야 합니다. 특별한 것은 없습니다. 이미 sys 모듈과 time 모듈을 다루어 보았습니다. (어떤 면에서 클래스 정의도 자신만의 이름 공간을 만듭니다. 그래서, 클래스의 메쏘드나 특성에 접근하려면 실체 변수의 이름을 사용하거나 클래스 이름을 먼저 사용해야 합니다. 더 자세한 것은 OOP 주제에서 다루겠습니다)

파이썬에서 총 네가지 이름공간이 가능합니다(또는 영역( scopes)):

1.     내장 영역 - 파이썬 자체에 정의된 이름들. 이 이름들은 언제나 프로그램 어디에서든 사용할 수 있다.

2.     모듈 영역 - 정의된 이름. 그러므로 파일이나 모듈 안에서만 보인다. 혼란스럽게도 이것이 파이썬에서 전역(global) 영역이라고 지칭된다. 반면에 다른 언에서는 보통 전역이란 어느 곳에서도 볼 수 있다는 뜻이다.

3.     지역 영역 - 함수나 클래스 메쏘드 안에 정의된 이름

4.     내포 영역 - 약간 복잡한 주제이다. 나중에 다른 주제에서 다시 다루겠다. 지금은 그냥 무시해도 좋다!

(내포 영역은 제외하고) 이 모든 것들이 포함된 예제 코드를 하나 살펴 봅시다:

def square(x):
   return x*x
data = int(raw_input('제곱할 숫자를 타자하시오: ')
print data, '를 제곱한 값은 ', square(data), '입니다'

다음 표에 각 이름과 그가 속하는 영역이 나열되어 있습니다:

이름

이름공간

square

모듈/전역

x

지역 (square에 속함)

data

모듈/전역

int

내장

raw_input

내장

defprint는 이름으로 취급하지 않습니다. 키워드나 명령어로서 언어의 구성요소를 이루기 때문에 변수의 이름이나 함수 이름으로 사용하려고 하면 에러를 맞이합니다.

지금까지는 그럭저럭 괜찮습니다. 이제 다른 이름공간에 있는 변수가 이름이 같을 경우 어떻게 이것이 함께 모일 수 있을까? 다른 말로 해서 현재 이름공간에 없는 이름을 참조해야 할 경우 어떻게 하는가?

현재 이름공간 바깥에 있는 이름 접근하기

여기에서 심지어 사용중인 이름이 현재 이름공간에 없는 경우라도 정확하게 어떻게 파이썬이 이름을 찾는지 좀 더 자세히 살펴보겠습니다. 다음과 같이 이름공간이 결정됩니다. 파이썬은 다음 순서로 검색합니다:

1.     지역 이름공간 안에서 (현재 함수),

2.     모듈 영역 안에서 (현재 파일),

3.     내장 영역.

그러나 이름이 다른 모듈에 있으면 어떻게 할까? , 그 모듈을 반입(import)합니다. 이미 이 자습서에서 여러 번 본 바와 같이 말입니다. 모듈을 반입하면 실제로 그 모듈의 이름(name)이 모듈 이름공간에 보입니다. 그러면 그 모듈 이름을 사용하여 친숙한 module.name 스타일로 그 모듈 안에 있는 변수 이름에 접근할 수 있습니다. 이 때문에 일반적으로 한 모듈로부터 이름을 모두 현재 파일 안으로 반입하는 것은 좋은 생각이 아닙니다: 모듈 변수가 이름이 같을 수 있기 때문에 위험합니다. 그리고 그 중에 하나가 다른 변수를 가려버려서 프로그램에서 이상한 행위를 야기할 수 있습니다.

예를 들어 두 개의 모듈을 정의해 봅시다. 두 번째 모듈이 첫 번째 모듈을 반입합니다:

##### module first.py #########
spam = 42
def print42(): print spam
###############################
##### module second.py ########
from first import *  # 앞 모듈에서 모든 이름을 반입한다 
spam = 101   # spam 변수를 만들어, 앞 버전의 변수를 가린다
print42()    # 무엇이 인쇄되는가? 42인가 101인가? 
################################

101이 인쇄될 것이라고 생각했다면 틀린 답입니다 (솔직히 예제를 처음 작성했을 때 저도 101이 인쇄될 거라고 예측했었습니다!).대신 42가 인쇄되는 이유는 예전에 미가공 재료 주제에서 설명드린 바와 같이 파이썬의 변수의 정의와 관련되어 있습니다. 이름은 단순히 객체를 가리키는데 사용되는 꼬리표일 뿐이라는 사실을 기억하세요. 이제 첫 모듈에서 print42라는 이름은 그 모듈에 정의된 함수 객체를 가리킵니다 (이것이 이상하게 들린다면 고급 주제인 기능적 프로그래밍에서 더 자세하게 설명합니다. 거기에서 람다 표현식(lambda expression)이라는 것을 연구합니다). 그래서 이름은 모듈 안으로 반입했더라도 실제 함수는 반입하지 않았습니다. 실제 함수는 여전히 자신만의 spam 버전을 가리키고 있습니다. 그리하여 새로 spam 변수를 만들더라도 print42가 가리키는 함수에 영향을 미치지 않습니다.

그 모든 혼란을 보면 타자수가 더 들기는 하지만 점 표기법으로 외부 모듈에 있는 이름에 접근하는 것이 더 안전한 이유를 예시해 보여줍니다. 나중에 만나게 될 Tkinter와 같이 보통 이름을 모조리 반입하는 모듈이 몇 가지 있습니다. 그러나 그런 모듈은 이름 충돌의 위험을 최소한으로 하여 작성됩니다. 물론 그 위험은 언제나 존재하며 찾기가 아주 어려운 버그를 만들어 낼 수 있습니다.

마지막으로 모듈에서 이름을 안전하게 반입하는 방법이 하나 더 있습니다. 다음과 같이:

from sys import exit

여기에서는 exit 함수만 지역 이름공간으로 가져옵니다. 다른 sys 이름은 사용할 수 없으며, 심지어 sys 그 자체도 사용할 수 없습니다!

이름 충돌 피하는

함수가 X라는 변수를 가리키고 그 함수 안(지역 영역) X가 존재하면 그것이 선택되고 사용됩니다. 이름 충돌을 막는 것은 프로그래머의 임무입니다. 이름이 같은 지역변수와 모듈 변수가 같은 함수 안에 함께 있을 수 없습니다 - 지역 변수는 모듈 변수를 가립니다.

그냥 전역 변수를 함수 안에서 읽고 싶다면 문제가 없습니다. 파이썬은 단순히 그 이름을 지역적으로 찾아 보고 발견하지 못하면 전역적으로 찾습니다 (필요하다면 내장 이름공간에서도 찾습니다). 문제는 값을 전역 변수에 할당하고 싶을 때 일어납니다. 그렇게 하면 보통 새로운 지역 변수를 함수 안에 만들어냅니다. 그래서, 이름이 같은 지역 변수를 만들지 않고 어떻게 값을 전역 변수에 할당할 수 있을까? global 키워드를 사용하면 할당할 수 있습니다:

var = 42
def modGlobal():
   global var  # 지역 변수가 생성되는 것을 방지한다.
   var = var - 21
def modLocal():
   var = 101
print var   # 42를 인쇄한다.
modGlobal()
print var   # 21을 인쇄한다.
modLocal()
print var   # 여전히 21을 인쇄한다.

여기에서 전역 변수가 modGlobal 함수에 의하여 바뀐 것을 알 수 있습니다. 그러나 modLocal 함수로는 바뀌지 않습니다. 뒤의 함수는 그냥 자신만의 내부 변수를 만들고 거기에 값을 할당합니다. 함수의 끝에서 그 변수는 쓰레가 수거되고 그의 존재는 모듈 수준에서 보이지 않습니다.

일반적으로 'global' 서술문은 자제하여 사용하는 것이 좋습니다. 보통 그 변수를 매개변수로 건네주고 그리고 수정된 변수를 돌려받는 것이 편이 더 좋습니다. 다음은 global 서술문을 회피하여 위의 modGlobal 함수를 재작성한 것입니다:

var = 42
def modGlobal(aVariable):
    return aVariable - 21
print var
var = modGlobal(var)
print var

이 경우 함수로부터 돌려받은 값을 원래 변수에 할당하면서 동시에 그것을 인자로 건네기도 합니다. 그 결과는 같지만 함수는 이제 밖의 코드에 얽매이지 않습니다 - 이 때문에 다른 프로그램에서 훨씬 더 쉽게 재사용할 수 있습니다. 또 어떻게 전역 변수가 변하는지 훨씬 더 쉽게 볼 수 있습니다 - 또 명시적인 할당이 일어나는 것을 볼 수 있습니다.

이 모든 것이 작동하는 것을 다음 예제에서 볼 수 있습니다 (이 예제는 순전히 요점을 예시해 보이기 위한 것입니다!):

# 모듈 영역의 변수
W = 5
Y = 3
# 매개변수는 함수 변수와 비슷하다.
# 그래서 X는 지역 영역에 있다.
def spam(X):    
   # 함수에게 모듈 수준을 들여다 보고 자신만의 W를 만들지 말라고 지시한다.
   global W   
   Z = X*2 # 새로 변수 Z가 지역 영역에 생성되었다.
   W = X+5 # 모듈 W는 위에 지시한대로 사용하였다.
    if Z > W:
      # pow '내장-영역' 이름이다.
      print pow(Z,W)
      return Z
   else:
      return Y # Y는 지역영역에 없으므로 모듈 버전을 사용한다.

 

기억해야 할 것

· 영역과 이름공간은 같은 것을 가리키는 다른 용어이다.

· 그 개념은 일상 언어와 같지만 정밀한 규칙은 달라질 수 있다.

· 파이썬은 세 가지 영역이 있다 - 파일 (전역), 함수 (지역) 그리고 내장.

· VBScript 그리고 JavaScript는 두 가지 영역이 있다 - 파일 (전역) 그리고 함수 (지역).

 

출처 : http://coreapython.hosting.paran.com/tutor/index.htm

'Python > 파이썬 프로그래밍 연습' 카테고리의 다른 글

객체 지향 프로그래밍  (0) 2012.04.24
정규 표현식  (0) 2012.04.24
에러 처리하기  (0) 2012.04.24
텍스트 처리하기  (0) 2012.04.24
주소록 심화연구  (0) 2012.04.24

댓글