###2019.03.06 TIL
(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)
대답해보기
========================
- 전역변수, 지역변수
- 함수에서 mmutable, immutable에 대해서
function
- 예를 바탕으로 설명을 진행한다.
>>> a = 10
>>>
>>> def func():
>>> a = 20
>>> def inner():
>>> global a
>>> a = 30
>>>
>>> print(a) # a = 10 출력 (func()가 실행되지 않음)
>>>
>>> def func():
>>> a = 20
>>> def inner():
>>> global a
>>> a = 30
>>>
>>> func() # func()의 a는 global a를 참조하고 있지 않다.
>>> print(a) # a = 10 출력 (func()는 실행되었으나 inner()가 실행되지 않음)
>>>
>>> def func():
>>> global a
>>> a = 20
>>> def inner():
>>> a = 30
>>>
>>> func()
>>> print(a) # a = 20 출력 (func()는 실행되었으나 inner()가 실행되지 않음)
>>>
>>> def func():
>>> global a
>>> a = 20
>>> def inner():
>>> a = 30
>>> inner()
>>>
>>> func() # inner의 a는 global a를 참조하고 있지 않다.
>>> print(a) # a = 20 출력 (inner()역시 실행되나 , global a를 참조하고 있지 않다)
>>>
>>> def func():
>>> a = 20
>>> def inner():
>>> global a
>>> a = 30
>>> inner()
>>>
>>> func() # inner의 a는 global a를 참조하고 있다.
>>> print(a) # a = 30 출력 (func()를 실행하며 inner() 역시 같이 실행 되었다.)
>>>
>>>
- 참조를 위해서는 계속 namespace를 위로 이동가능하다.
- 하지만 수정을 위해서는 단계에 따라 꼭 nonlocal 혹은 global 이라는 호출이 필요하다.
- 네임 스페이스는 local namespace —> global namespace —> Built - in namespace로 올라간다.
네임스페이스에 관한 예를 더 들어보자
>>> a = 10
>>> def outer():
>>> b = 20
>>> def inner1():
>>> b = 30
>>> def inner2():
>>> d = 40
>>> b = 50
>>> print(b)
>>> ineer2()
>>> print(b)
>>> inner1()
>>> outer() # 50 와 30이 출력된다.
>>>
>>>
>>> a = 10
>>> def outer():
>>> b = 20
>>> def inner1():
>>> b = 30
>>> def inner2():
>>> d = 40
>>> nonlocal b # local namesapce의 b를 참조한다.
>>> b = 50
>>> print(b)
>>> inner2()
>>> print(b)
>>> inner1()
>>> outer() # 50 와 50이 출력된다.
>>>
>>>
위의 이야기를 명확하게 이해할 수 있으면 namespace에 대한 개념이 좀 더 명확해졌다고 할 수 있다.
함수의 스텍프레임
함수를 실행하게 되면 메모리에 스텍프레임이라는 공간이 형성되고 스텍프레임 안에 변수와 값들이 저장되게 된다.
이 스텍프레임은 함수 호출이 끝나면 사라진다. 스텍프레임은 메모리를 공부한 이후에 좀 더 깊게 살펴보기로 한다.
>>> g_var = 20
>>> def func(val):
>>> val += 100
>>> return val
>>>
>>> func(g_var) # 120이라는 return 값을 낸다.
>>> print(g_var) # 20이 출력된다.
>>>
>>>
위의 함수를 실행함으로 g_var는 120으로 바뀌지 않는다. 왜 그럴까?
처음 g_var = 20 이라고 할당되므로서 20이라는 값 객체가 메모리 저장된다. g_var는 그 20이라는 값 객체를 가르키는 변수이다. func(g_var)를 실행하게 되면 func라는 스텍프레임이 생기게 되고 그 스텍프레임 안에서 val라는 새로운 변수가 20을 가르키게 된다. 숫자는 immutable하기 때문에 그 val에 100을 더해 120이라는 새로운 값 객체를 만들어서 가르키게 된다. 그리고 함수 실행이 끝나게 되면 func의 스텍프레임이 사라지게 되므로 val = 120이라는 값도 사라지게 된다. 따라서 g_var는 그대로 20이 출력되게 된다.
위의 그림이 100% 정확하진 않지만 이해하는데 도움은 줄 수 있다.(파이썬에서는 값이 저렇게 들어가는 것이 아니라 가리키는 형태로 나와야한다.)
어떤 사람은 g_var가 120으로 바뀌었으면 할 것이고, 또 누군가는 g_var는 그대로 두되 저 함수를 통해서 실행된 값들을 받고 싶을 것이다.
2가지의 해결책을 보자
- g_var의 값을 120으로 바꾸기
>>> g_var = 20
>>> def func():
>>> global g_var
>>> g_var += 100
>>> return val
>>>
>>> func()
>>> print(g_var) # 120이 출력된다.
>>>
전역 변수를 불러옴으로서 해결할 수 있다.
- g_var는 그대로 두되 저 함수를 통해서 실행된 값들을 받자.
>>> g_var = 20
>>> def func(val):
>>> val += 100
>>> return val
>>>
>>> a = func(g_var) # 120이라는 return 값을 낸다.
>>> print(a) # 120이 출력된다.
>>> print(g_var) # 20이 출력된다.
>>>
>>>
해결책은 간단하다. 새로운 변수 a를 통해 func(g_var)의 값을 받아오면 된다.
그럼 여기서 또 하나의 의문이 생긴다. 과연 mutable한 list나 dictionary가 오게 되면 바뀔까?
한번 해보면 된다 ㅎㅎ.
>>>
>>> li1 = [1,2,3]
>>> def func(li, i):
>>> li.append(i)
>>> return li
>>>
>>> func(li1, 4)
>>> print(li1) # [1,2,3,4]
>>>
>>>
너무나도 쉽게 변경되는 것을 알 수 있다.