https://wikidocs.net/23109



네임스페이스의 종류


파이썬의 네임스페이스는 3가지로 분류할 수 있다:

  • 전역 네임스페이스: 모듈별로 존재하며, 모듈 전체에서 통용될 수 있는 이름들이 소속된다.
  • 지역 네임스페이스: 함수 및 메서드 별로 존재하며, 함수 내의 지역 변수들의 이름들이 소속된다.
  • 빌트인 네임스페이스: 기본 내장 함수 및 기본 예외들의 이름들이 소속된다. 파이썬으로 작성된 모든 코드 범위가 포함된다.

(출처: https://www.programiz.com/python-programming/namespace)

파이썬의 네임스페이스는 다음과 같은 특징들을 가지고 있다.

  • 네임스페이스는 딕셔너리 형태로 구현된다.
  • 모든 이름 자체는 문자열로 되어있고 각각은 해당 네임스페이스의 범위에서 실제 객체를 가리킨다.
  • 이름과 실제 객체 사이의 매핑은 가변적(Mutable)이므로 런타임동안 새로운 이름이 추가될 수 있다.
  • 다만, 빌트인 네임스페이스는 함부로 추가하거나 삭제할 수 없다.

(출처: https://blog.confirm.ch/python-namespaces/)

위의 그림은 파이썬의 네임스페이스가 어떤 것인지 잘 표현해준다. 파이썬 공식 홈페이지의 튜토리얼에서는 네임스페이스를 이름들과 실제 객체들 사이의 매핑이라고 정의한다.




네임스페이스

변수에 객체를 할당하면, 변수와 변수에 연결된 객체는 딕셔너리형태(key, value)로 연결되어 네임스페이스 안에 저장됩니다.

a = 3
print(globals())

실행결과

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

전역(globals) 네임스페이스를 확인하는 globals함수를 실행하면 함수의 네임스페이스를 볼 수 있습니다. 정의한적 없는 변수들도 여럿 보입니다.
a라는 이름에 객체 3이 지정되있는걸 확인할 수 있습니다.

함수는 전역과 별개의 네임스페이스를 가집니다.

a = 3

def func():
    a = 100
    b = 'python'

func()
print(globals())

함수안에서 변수 a의 값을 바꾸고, 새로운 변수 b를 생성했지만, globals의 네임스페이스에는 바뀐 값과, 새로 할당한 변수이름이 보이지 않습니다.

함수는 함수만에 별도 구분된 네임스페이스 공간을 가집니다.

a = 3
c = 'Hello'

def func():
    a = 100
    b = 'python'
    print('함수 내부 namespace', locals())

func()

locals() 함수는 실행하는 공간에 namespace를 출력해줍니다. 함수안에서 locals함수를 실행했기 때문에, 함수안에 namespace를 출력해줍니다.

만약 함수 밖에서, 즉 전역에서 locals함수를 실행한다면 globals함수를 실행한 결과와 동일한 값을 출력합니다.

현재 코드기 실행되는 공간의 namespace를 리턴하는 locals함수를 실행시켜서 확인합니다.

실행결과

함수 내부 namespace {'b': 'python', 'a': 100}

이처럼 함수는 별도의 namespace를 가집니다. 만약 함수안에 함수를 또 생성한다면 어떻게 될까요?


a = 3
c = 'Hello'

def func():
    a = 100
    b = 'python'
    print('외부 함수 내부 namespace', locals())
    def inner_func():
        print('내부 함수 내부 namespace', locals())
    inner_func()

func()

실행결과

외부 함수 내부 namespace {'b': 'python', 'a': 100}
내부 함수 내부 namespace {}

함수는 다른 함수안에 있을지라도, 별도의 namespace를 가지는걸 확인할 수 있습니다.

네임스페이스 참조

네임스페이스가 다르다면, 값을 참조할 수 없습니다.

def func():
      a = 3

print(a)

실행결과

NameError: name 'a' is not defined

1

하지만 함수는 globals의 변수를 참조할 수 있습니다.

a = 100

def func():
      def inner():
            b = 'python'
      inner()
      print(a)
      print(b)

func()

실행결과

100
NameError: name 'b' is not defined

2

함수는 globals 네임스페이스뿐 아니라 상위 함수의 변수도 참조할 수 있습니다.

a = 'fastcampus'

def func1():
      b = 'python'
      def func2():
             print(a)
             print(b)
      func2()

func1()

실행결과

fastcampus
python

3

네임스페이스가 분리된 이유

함수별로 네임스페이스가 분리되어 있지 않고 전역과 함수내부에 네임스페이스가 하나로 관리된다면, 함수를 만드는데 복잡한 고려사항이 생기게됩니다.

네임스페이스가 변경되지 않으면, 함수내부에서 외부에 있는 변수가 참조하고 있는 객체를 변경할 수 있습니다.

a = 'python'
def func():
      a = 3

func()

print(a)

네임스페이스가 하나여서 함수내부에서 외부에 (전역 또는 외부함수)에 변수를 수정할 수 있게되면, 위 코드의 출력결과가 3이 됩니다.

네임스페이스가 하나라면 프로그래머는 복잡한 고려사항을 항상 생각하고 함수를 작성해야합니다. 바로 변수이름을 겹치지 않게 지어야 한다는 점입니다.

이러한 복잡한 고려사항을 생각해야하는 상황이라고 가정하고 다음의 코드를 실행시켜보겠습니다.

한변의 길이가 리스트로 주어져있을때, 그 변을 한변으로 하는 정사각형의 넓이를 구해서, 가장 큰 사각형의 넓이를 출력하는 코드입니다.

반복되는 연산인 정사각형의 넓이는 구하는 연산은 함수로 지정했습니다.

def square(n):
    result = n ** 2
    return result


lines = [4, 3, 9, 8]
result = 0
for line in lines:
    area = square(line)
    if area > result:
        result = area

print(result)

이러한 코드에서 square함수가 실행될때 마다 함수안에서 전역에 result변수의 값을 계속 바꾸고 있기 때문에 즉 함수내부에서 전역에 있는 result변수의 참조를 변경시킨다면 의도한 결과가 나오지 않게 됩니다.

모든 함수의 변수가 하나의 네임스페이스에서 관리된다면, 모든 함수를 만들때 변수를 겹치지 않게 만들어야하는 노력을 해야합니다.

파이썬에서 함수는, 함수의 로직에만 신경쓸수 있도록, 함수내부에서 외부에 변수를 참조는 가능하지만, 새로운 변수를 만든다면 변수를 별도의 공간에 저장하도록 네임스페이스를 분리하였습니다.

외부 네임스페이스 변수 값 변경

함수별로 변수를 저장하는 네임스페이스는 분리되어 있습니다. 하지만, 함수에서 외부에 있는 네임스페이스에 값은 참조가능합니다.

만약 함수내부에서 외부에 변수를 변경하고 싶다면 어떻게 해야할까요?

함수에서 외부에 변수이름과 동일한 이름의 새로운 변수를 만들 수 있습니다. 이때 외부에 변수는 영향을 받지 않습니다.

lang = 'jython'
def func():
      lang = 'python'
      print(lang)

func()
print(lang)

실행결과

python
jython

새로운 변수를 함수 local 네임스페이스에 생성하는게 아니라, 외부에 있는 변수를 변경하고 싶다면 global또는 nonlocal키워드를 사용해야 합니다.

lang = 'jython'
def func():
      global lang 
      lang = 'python'
      print(lang)

func()
print(lang)

실행결과

python
python

함수 내부에 선언한 global lang 부분은, 이름 lang을 global에 선언한 lang으로만 사용하겠다는 뜻입니다.

이후에 lang변수의 값을 변경하면, global에 있는 변수가 참조하고 있는 객체가 바뀌게 됩니다.

외부함수에 있는 값을 변경할때는 nonlocal키워드를 사용하면 됩니다.

def func():
      var = 1
      def func2():
            nonlocal var
            var = 3
      func2()
      print(var)

func()

실행결과

3


+ Recent posts