SW개발/Python

[Python]네임스페이스란(Namespace) ?

지난번 Zen of Python에 잠깐 언급된 파이썬의 네임스페이스에 대해 알아보는 포스팅을 써보려고 합니다.

 

지난 포스팅 -> Zen of Python 

https://leffept.tistory.com/396

 

[Python]The Zen of Python - 파이써닉한 코드

파이썬에서의 디자인 원칙을 잘 나타내는 The Zen of Python이라는 것이 존재한다. 여기에 존재하는 원칙들을 바탕으로 자신이 작성한 코드가 파이써닉한 코드인지 아닌지에 대해서도 생각해볼 수

leffept.tistory.com

 

네임스페이스란 ?

네임스페이스란 특정한 객체를 이름에 따라 구분할 수 있는 범위를 말합니다. 파이썬 내부의 모든 것들은 전부 객체로 구성되어 있고, 이들은 특정 이름들과의 매핑 관계를 가지고 있습니다. 이 매핑을 포함하고 있는 공간을 네임스페이스라고 합니다. 

네임스페이스의 내부는 키, 값 구조를 가지고 있습니다.

 

필요한 이유

프로그래밍을 하다보면 모두 unique한 네이밍을 정할 수는 없고, 이는 사실상 불가능 합니다.

따라서, 네임스페이스라는 공간의 분리를 통해 서로 다른 네임스페이스에 있다면 (ex: 서로 다른 모듈) 같은 이름의 네이밍이 있어도 아예 관계가 없는 것으로 간주할 수 있습니다.

 

네임스페이스 종류

  • 전역 네임스페이스 : 모듈별로 존재합니다. 모듈 전체에 통용되는 이름을 사용합니다.
  • 지역 네임스페이스 : 함수 및 메소드 별로 존재합니다. 함수 내의 지역 변수들이 소속됩니다.
  • 빌트인 네임스페이스 : 기본 내장 함수 및 기본 예외들의 이름이 저장됩니다.

네임스페이스는 총 3가지 종류가 있습니다. 실제 코드를 통해 각각의 네임스페이스들을 확인해봅시다.

 

전역 네임스페이스

>>> leffe = "leffe's tistory"
>>> print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>,
'__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>,
'leffe': "leffe's tistory"}

 

globals() 함수를 통해 전역 네임스페이스를 확인할 수 있습니다. 지정한 leffe 변수 이외의 것들도 여러개 보입니다.

 

지역 네임스페이스

>>> leffe = "leffe's tistory"
>>>
>>> def leffe():
...     leffe = "leffe's tistory in def"
...     print(locals())
...
>>> leffe()
{'leffe': "leffe's tistory in def"} # 지역 네임 스페이스
>>> print(globals()) # 전역 네임 스페이스
{'__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None,
'__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>,
'leffe': <function leffe at 0x7fa1301b7040>}

함수 안에서 지정한 변수의 경우는 전역 네임스페이스에 저장되지 않고, local() 함수를 통해 지역 네임스페이스에만 저장되는 것을 확인할 수 있습니다. 이 코드를 통해 같은 네이밍을 가진 변수를 선언하더라도, 네임스페이스 분리가 이루어져 문제없이 동작하는 것을 볼 수 있습니다.

 

빌트인 네임스페이스

>>> print(dir(__builtins__))
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
...]

위 코드를 통해 파이썬의 빌트인 네임스페이스에는 어떤 것들이 있는지 확인할 수 있습니다. 

만약, 빌트인 네임스페이스에 있는 것을 접근하여 변수의 네이밍으로 사용한다면 다음과 같은 에러를 마주할 수 있습니다.

>>> str = "leffe"
>>> print(str())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable

 

가장 기본적인 빌트인 함수인 str을 변수의 네이밍으로 새롭게 정의했습니다. 그 후 str()의 빌트인 함수를 접근하려는 경우 오류가 발생합니다. 위의 변수 선언을 통해 내장 빌트인 함수의 기능을 덮어씌웠기 때문입니다. 따라서, 문자열을 호출할 수 없다는 에러를 발생시킵니다.

이러한 이유 때문에 기본적으로 빌트인 함수의 네이밍은 코드에서 사용하지 않는 것입니다. 

 

네임스페이스 우선 순위

위에서 설명한 네임스페이스는 빌트인 > 전역 > 지역 순으로 우선순위를 가집니다. 그렇다면 지역 네임스페이스에서 전역 스페이스를 접근할 수 있을까요? 가능합니다.

>>> blog_name = "leffe's tistory"
>>> def leffe():
...     print(blog_name, "global namespace var")
...
>>> leffe()
leffe's tistory global namespace var

위처럼 간단하게 접근하여 사용할 수 있습니다. 그러나 전역 네임스페이스의 값을 변경하고 싶은 경우에는 추가적인 키워드가 필요합니다.

>>> blog_name = "leffe's tistory"
>>> def leffe():
...     global blog_name
...     blog_name = "Changed blog name"
...
>>> leffe()
>>> print(blog_name)
Changed blog name

global() 키워드를 통해 전역 네임스페이스에 접근한 값을 변경할 수 있습니다. 하지만 기본적으로 전역변수의 값을 지역 함수에서 변경하는 것은 많은 사이드 이펙트를 유발할 수 있으므로 추천되지 않습니다.

 

이와 비슷한 기능을 하는 nonlocal()의 경우에는, 코드가 실행되는 네임스페이스의 바로 윗 단계 네임스페이스에 접근합니다.

 

오늘은, 파이썬의 네임스페이스의 존재 이유와 사용법에 대해서 간단하게 알아보았습니다.

프로그래밍을 하다보면 자연스럽게 알게되는 부분인데 무슨 원리로 동작하는지 한번쯤 개념을 짚고 넘어가도 좋다고 생각합니다 :) 

 

읽어주셔서 감사합니다.

 

728x90