본문 바로가기
Python/INFO

[PYTHON]Namespace

by 오늘은강박사갈거야~~ 2021. 8. 23.
반응형

- 이 글은 제가 공부를 하며, 이해한 것을 바탕으로 작성하는 글입니다.

- 그렇기에 틀리거나 잘못된 부분이 있을 수 있습니다.

- 글의 오류를 발견하시면, 댓글로 말씀해 주시면 정말 감사하겠습니다.


1. namespace란

우선 공식 docs의 문서를 살펴보면 아래와 같은 정의로 되어있다. 

네임스페이스는 이름에서 개체로 매핑됩니다.

 파이썬의 가장 큰 특징은 모든 것이 객체로 구성되어있다. 이것들은 모두 이름을 가질 수 있다. 즉, 문자, 숫자, 리스트, 함수 등 모든 것에 이름을 붙일 수 있다는 이야기로 이해할 수 있을 것이다. 이때 설정한 객체와 우리가 만든 그 객체의 이름을 매핑 해 놓은 것들을 가지고 있는 공간을 namespace라고 한다. 이 namespace는 파이썬의 자료구조인 딕셔너리 형태로 되어있다.

 

namespace는 전역(Global), 지역(Local), 빌트인(Built-in) 이렇게 3가지로 구분 지어진다.

  • Global: 모듈마다 존재하고, 모듈 전체에서 통용될 수 있는 이름들
  • Local: 함수 및 메서드마다 있고 함수 내의 지역 변수들의 이름들
  • Enclosing: 클로저에 할당된 이름(함수 내부에 함수)
  • Built-in: 기본 내장 함수 등과 같은 이름들. 파이썬으로 작성된 모든 코드 범위를 가짐.

 

아래 기본적으로 내장되어 있는 built-in namespace 목록을 확인해 본 것이다.

 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', 'ModuleNotFoundError', 'NameError', 'None',
 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning',
 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', '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__', '__spec__', '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']

 

도대체 무슨 말인지 이해하기 쉽지 않다. 뭔가 전역, 지역이라는 단어가 나오니, 사용되는 범위에 관련된 내용인 거 같기도 하다. global, local, enclosing 순으로 하나씩 예시를 들어 알아보자.

 

 

- Global

예시를 보자. globals()라는 함수는 이 파일의 global namespace를 보여주는 함수이다. 

a = 729

print(globals())
# 결과

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018B074F6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'c:\\test.py', '__cached__': None, 'a': 729}

결과에 729라는 숫자 객체에 a라는 이름이 잘 할당되어있다. 

 

- Local

이번 예시는 함수를 하나 만들었고, 내부에 a, b값을 할당하였다. 예상되는 것은 당연히 a는 950이고 b는 110일 것이다. 

a = 910

def function():
    a = 950
    b = 110

function()
print(globals())
# 결과 

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000025BE5F46CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'c:\\test.py', '__cached__': None, 'a': 910, 'function': <function function at 0x0000025 BE5 F8 DF70>}

 

결과는 예상과 다르게, 값이 변한 게 없고, a는 아직도 910이라는 문자 객체를 가리키고 있다. 함수 내부는 다른 공간으로 여겨지는 것이다. 이 공간은 위에서 언급했던 것과 같은 Local namespace라고 할 수 있다. 아래 예시를 보고 확인해 보자.

 

local()은 local namespace를 출력하는 함수이다. 

 

a = 910

def function():
    a = 950
    b = 110
    print(locals())
# 결과는

{'a': 950, 'b': 110}

결과는 1줄에 있는 a와 함수 내부의 a는 숫자 객체에 할당된 이름만 같을 뿐이지, 서로 다른 namespace에 존재하는 것이다.

 

- Enclosing

이번에는 함수 내부에 또 다른 함수를 만들어 보고, global, local, enclosing을 모두 확인해보자.

va1 = 9501
va2 = 2420

def function():
    val1 = 421
    val2 = 123
    val3 = 123456
    print('1번 namespace', locals())
    def function_into_function():
        val3 = 101
        print('2번 namespace', locals())
    
    
    function_into_function()
    
function()

print(globals())

 

#결과

1번 namespace {'val1': 421, 'val2': 123, 'val3': 123456}
2번 namespace {'val3': 101}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000164C84D6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'c:\\test.py', '__cached__': None, 'va1': 9501, 'va2': 2420, 'function': <function function at 0x00000164CA00DF70>}

 

결과는 함수 내부의 함수일지라도 모든 namespace가 구분되는 것을 알 수 있다.

 

 

2. namespace의 참조

 

namespace의 영역에 따라, 하위 namespace는 상위 namespace를 참조할 수 있지만, 거꾸로는 그렇지 못하다.

이는 간단한 예시로 확인이 가능하다. 

val4 = 123
def function():
    val1 = 1
    val2 = 2
    val3 = 3
    def function_into_function():
        print(val1+1)
        print(val2+1)
        print(val3+1)    
        print(val4)
    function_into_function()
    
function()

print(val1)

 

#결과

2
3
4
123
Traceback (most recent call last): File "c:\test.py", line 15, in <module> print(val1) NameError: name 'val1' is not defined

 

함수 내부의 함수인 function_to_function에서는 상위 함수인 function에서 정의한 변수들을 사용할 수 있다. 이유는 function과 function_to_function이 동일한 local namespace에 존재하기 때문이다. 그리고 바깥의 global영역에서 val1을 출력하니, 정의되지 않은 함수로 나오게 된다. 이는 상위 namespace에서 하위 namespace의 것을 참조가 불가능하기 때문이다.

 

거꾸로 하위 namespace에서 상위 namespace로는 참조가 가능한데, 이는 global에서 선언한 val4의 값을 함수 내부의 함수에서 출력이 가능한 것을 보면 확인이 가능하다.

 

네임스페이스가 필요한 이유는 다음과 같다. 프로그래밍을 수행하다 보면 모든 변수 이름과 함수 이름을 정하는 것이 중요한데 이들 모두를 겹치지 않게 정하는 것은 사실상 불가능하다

 

 

3. 결론

 namespace가 없으면 모든 변수의 이름을 다르게 작성해야 한다. 이는 거의 불가능에 가깝다. 따라서 namespace라는 개념을 만들어, 내가 만든 하나의 이름이 어디에서 통용될 것인지에 대한 범위를 한정 짓게 되는 것이다.

 

 

4. 참고문헌

- 참고자료1

- 참고자료2

반응형

댓글