지난번에 이것 저것 찾아가면서 나름대로 python 변수와 스코프에 대해 정리해봤는데, 여전히 머릿속에 의문이 가시질 않아서 다른 포스팅을 찾아서 재정리한다.
1. 이름공간(네임스페이스)이란?
- 이름공간이란 변수가 소속되어 있는 영역이다.
- 보통 변수에 따라 구분된다.
- 이름공간에서는 변수와 객체의 상대관계는 사전으로 보존되어 있다. (여기서 '사전'은 locals()함수로 확인 가능하다.)
- 모듈을 가진 이름공간을 global이라고 말하고, 어디에서든 globals()로 확인 가능하다.
- global은 같은 모듈내라면 어디에서든 접근할 수 있다.
예를 들어,
def a():
# 함수 a 내부의 이름 공간
l = 0
s = 'apple'
def b():
# 함수 b내부의 이름 공간
m = 1
t = 'banana'
print('in function b:', locals())
print('in global from b:', globals())
print('in function a:',locals())
b()
# global의 이름공간
n = 2
u = 'cake'
a()
print('in global:',globals())
"""
[출력 결과]
in function a: {'b': <function a.<locals>.b at 0x7fa8e63dc488>, 's': 'apple', 'l': 0}
in function b: {'t': 'banana', 'm': 1}
in global from b: {'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', "name = input('Who are you?')\nprint('Welcome', name)", 'astr = "123"\n\ntry:\n print("Hello")\n islnt = int(astr)\n print("World")\nexcept:\n islnt = "Integer로 변환할 수 없습니다."\n\nprint(\'Done\', islnt)', "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing", "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing()", "while True:\n line = input('> ')\n if line[0] == '#' :\n continue\n if line == 'done' :\n break\n print(line)\nprint('Done!')", 'a = 2 \n\ndef test():\n a = 3\n print("inside test:", a)\n\ntest()', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test:, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test":, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', "def a():\n # 함수 a 내부의 이름 공간\n l = 0\n s = 'apple'\n\n def b():\n # 함수 b내부의 이름 공간\n m = 1\n t = 'banana'\n print('in function b:', locals())\n print('in global from b:', globals())\n\n print('in function a:',locals())\n b()\n\n# global의 이름공간\nn = 2\nu = 'cake'\na()\nprint('in global:',globals())"], '_oh': {3: <function thing at 0x7fa8ee0cbc80>}, '_dh': ['/content'], '_sh': <module 'IPython.core.shadowns' from '/usr/local/lib/python3.6/dist-packages/IPython/core/shadowns.py'>, 'In': ['', "name = input('Who are you?')\nprint('Welcome', name)", 'astr = "123"\n\ntry:\n print("Hello")\n islnt = int(astr)\n print("World")\nexcept:\n islnt = "Integer로 변환할 수 없습니다."\n\nprint(\'Done\', islnt)', "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing", "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing()", "while True:\n line = input('> ')\n if line[0] == '#' :\n continue\n if line == 'done' :\n break\n print(line)\nprint('Done!')", 'a = 2 \n\ndef test():\n a = 3\n print("inside test:", a)\n\ntest()', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test:, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test":, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', "def a():\n # 함수 a 내부의 이름 공간\n l = 0\n s = 'apple'\n\n def b():\n # 함수 b내부의 이름 공간\n m = 1\n t = 'banana'\n print('in function b:', locals())\n print('in global from b:', globals())\n\n print('in function a:',locals())\n b()\n\n# global의 이름공간\nn = 2\nu = 'cake'\na()\nprint('in global:',globals())"], 'Out': {3: <function thing at 0x7fa8ee0cbc80>}, 'get_ipython': <bound method InteractiveShell.get_ipython of <google.colab._shell.Shell object at 0x7fa8f38cf550>>, 'exit': <IPython.core.autocall.ZMQExitAutocall object at 0x7fa8f0e944e0>, 'quit': <IPython.core.autocall.ZMQExitAutocall object at 0x7fa8f0e944e0>, '_': <function thing at 0x7fa8ee0cbc80>, '__': '', '___': '', '_i': 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_ii': 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_iii': 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i1': "name = input('Who are you?')\nprint('Welcome', name)", 'name': 'kim', '_i2': 'astr = "123"\n\ntry:\n print("Hello")\n islnt = int(astr)\n print("World")\nexcept:\n islnt = "Integer로 변환할 수 없습니다."\n\nprint(\'Done\', islnt)', 'astr': '123', 'islnt': 123, '_i3': "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing", 'thing': <function thing at 0x7fa8ee0cbea0>, '_3': <function thing at 0x7fa8ee0cbc80>, '_i4': "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing()", '_i5': "while True:\n line = input('> ')\n if line[0] == '#' :\n continue\n if line == 'done' :\n break\n print(line)\nprint('Done!')", 'line': 'done', '_i6': 'a = 2 \n\ndef test():\n a = 3\n print("inside test:", a)\n\ntest()', 'a': <function a at 0x7fa8e63dc048>, 'test': <function test at 0x7fa8edff28c8>, '_i7': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test:, a)', '_i8': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test":, a)', '_i9': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test", a)', '_i10': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', '_i11': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', '_i12': 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i13': 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i14': 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i15': "def a():\n # 함수 a 내부의 이름 공간\n l = 0\n s = 'apple'\n\n def b():\n # 함수 b내부의 이름 공간\n m = 1\n t = 'banana'\n print('in function b:', locals())\n print('in global from b:', globals())\n\n print('in function a:',locals())\n b()\n\n# global의 이름공간\nn = 2\nu = 'cake'\na()\nprint('in global:',globals())", 'n': 2, 'u': 'cake'}
in global: {'__name__': '__main__', '__doc__': 'Automatically created module for IPython interactive environment', '__package__': None, '__loader__': None, '__spec__': None, '__builtin__': <module 'builtins' (built-in)>, '__builtins__': <module 'builtins' (built-in)>, '_ih': ['', "name = input('Who are you?')\nprint('Welcome', name)", 'astr = "123"\n\ntry:\n print("Hello")\n islnt = int(astr)\n print("World")\nexcept:\n islnt = "Integer로 변환할 수 없습니다."\n\nprint(\'Done\', islnt)', "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing", "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing()", "while True:\n line = input('> ')\n if line[0] == '#' :\n continue\n if line == 'done' :\n break\n print(line)\nprint('Done!')", 'a = 2 \n\ndef test():\n a = 3\n print("inside test:", a)\n\ntest()', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test:, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test":, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', "def a():\n # 함수 a 내부의 이름 공간\n l = 0\n s = 'apple'\n\n def b():\n # 함수 b내부의 이름 공간\n m = 1\n t = 'banana'\n print('in function b:', locals())\n print('in global from b:', globals())\n\n print('in function a:',locals())\n b()\n\n# global의 이름공간\nn = 2\nu = 'cake'\na()\nprint('in global:',globals())"], '_oh': {3: <function thing at 0x7fa8ee0cbc80>}, '_dh': ['/content'], '_sh': <module 'IPython.core.shadowns' from '/usr/local/lib/python3.6/dist-packages/IPython/core/shadowns.py'>, 'In': ['', "name = input('Who are you?')\nprint('Welcome', name)", 'astr = "123"\n\ntry:\n print("Hello")\n islnt = int(astr)\n print("World")\nexcept:\n islnt = "Integer로 변환할 수 없습니다."\n\nprint(\'Done\', islnt)', "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing", "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing()", "while True:\n line = input('> ')\n if line[0] == '#' :\n continue\n if line == 'done' :\n break\n print(line)\nprint('Done!')", 'a = 2 \n\ndef test():\n a = 3\n print("inside test:", a)\n\ntest()', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test:, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test":, a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', "def a():\n # 함수 a 내부의 이름 공간\n l = 0\n s = 'apple'\n\n def b():\n # 함수 b내부의 이름 공간\n m = 1\n t = 'banana'\n print('in function b:', locals())\n print('in global from b:', globals())\n\n print('in function a:',locals())\n b()\n\n# global의 이름공간\nn = 2\nu = 'cake'\na()\nprint('in global:',globals())"], 'Out': {3: <function thing at 0x7fa8ee0cbc80>}, 'get_ipython': <bound method InteractiveShell.get_ipython of <google.colab._shell.Shell object at 0x7fa8f38cf550>>, 'exit': <IPython.core.autocall.ZMQExitAutocall object at 0x7fa8f0e944e0>, 'quit': <IPython.core.autocall.ZMQExitAutocall object at 0x7fa8f0e944e0>, '_': <function thing at 0x7fa8ee0cbc80>, '__': '', '___': '', '_i': 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_ii': 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_iii': 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i1': "name = input('Who are you?')\nprint('Welcome', name)", 'name': 'kim', '_i2': 'astr = "123"\n\ntry:\n print("Hello")\n islnt = int(astr)\n print("World")\nexcept:\n islnt = "Integer로 변환할 수 없습니다."\n\nprint(\'Done\', islnt)', 'astr': '123', 'islnt': 123, '_i3': "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing", 'thing': <function thing at 0x7fa8ee0cbea0>, '_3': <function thing at 0x7fa8ee0cbc80>, '_i4': "def thing():\n print('Hello')\n print('Fun')\n\nthing()\nprint('Zip')\nthing()", '_i5': "while True:\n line = input('> ')\n if line[0] == '#' :\n continue\n if line == 'done' :\n break\n print(line)\nprint('Done!')", 'line': 'done', '_i6': 'a = 2 \n\ndef test():\n a = 3\n print("inside test:", a)\n\ntest()', 'a': <function a at 0x7fa8e63dc048>, 'test': <function test at 0x7fa8edff28c8>, '_i7': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test:, a)', '_i8': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test":, a)', '_i9': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest\nprint("after test", a)', '_i10': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', '_i11': 'a = 2 \n\ndef test():\n print("inside test", a)\n\ntest()\nprint("after test", a)', '_i12': 'a = 2 \n\ndef test():\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i13': 'a = 2 \n\ndef test()\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i14': 'a = 2 \n\ndef test():\n global a\n print("inside test", a)\n a = 3\n\ntest()\nprint("after test", a)', '_i15': "def a():\n # 함수 a 내부의 이름 공간\n l = 0\n s = 'apple'\n\n def b():\n # 함수 b내부의 이름 공간\n m = 1\n t = 'banana'\n print('in function b:', locals())\n print('in global from b:', globals())\n\n print('in function a:',locals())\n b()\n\n# global의 이름공간\nn = 2\nu = 'cake'\na()\nprint('in global:',globals())", 'n': 2, 'u': 'cake'}
"""
와 같이 작성한다면, 함수 a의 내부, 함수 b 내부와 global로 전부 3 개의 이름 공간이 생성된다.
- 함수 a 내부에서는{'b': <function a.<locals>.b at 0x7fa8e63dc488>, 's': 'apple', 'l': 0}
- 함수 b 내부에서는 {'t': 'banana', 'm': 1}
- 함수 b 내부에서 본 global은 {a: 함수 객체a, n: 2, u: 'cake', 등}
- global에서는 {a : 함수객체 a, n: 2, u : 'cake', 등}
2. Scope란?
- Scope란 어떠한 이름공간으로 부터 (직접) 접근가능한 이름 공간의 범위이다.
- 직접 접근이란 a.b와 같이 '.'를 사용하여 상관관계를 지정할 필요 없이, 단순히 'b'와 같이 작성하는 것으로 접근하는 방법이다.
- Scope는 '시야'와 같은 것으로 어떤 이름공간이 생겼을 때, 그곳에서 볼 수 있는 이름공간은 어디까지인가에 대한 이야기이다.
- 기본적으로 자신보다 바깥쪽의 변수가 Scope에 포함된다.
- Scope에는 몇 가지 종류가 있다.
1) Scope의 우선순위
- 변수를 평가할 때, 그것을 참고하는 객체를 찾기 위해서 그 변수를 가진 이름공간의 검색하는 것을 시작한다.
- 아무것도 지정되지 않은 경우 아래와 같은 순서대로 변수를 검색한다.
① local(자신의 함수내) ② nonlocal(local도 global도 아닌 중간 영역)의 중, 보다 local에 가까운 내부쪽부터 먼저 검색된다. ③ global(자신의 모듈) ④ built-ins(len(), print(), str()등 이미 존재하는 함수), 더 정확히 말하자면 글로벌 변수인 __builtins__의 속성을 검색한다. |
print(spam)가 어떠한 이름공간으로 부터 본 scope는 아래와 같다.
- builtins는 import builtins로 명시적으로 import 가능하다.
- builtins는 global보다 우선 순위가 낮다.
import builtins
builtins.spam = "hello"
print(spam)
#=>hello
spam = "python"
print(spam)
#=>python
2) 명시적인 Scope의 지정
- 지정가능한 Scope의 종류는 2 종류로 각각은 위에서 언급한 계층에 대응한다.
① nonlocal
② global
- local이 아닌 변수에 대입할 때는 새로운 local의 변수를 만들어버리는 것처럼 scope를 지정하지 않으면 안된다.
spam = 0
def a():
# global를 변경하려고 해도, local에 새로운 변수가 생겨버리고 만다.
spam = 1
a()
print(spam) # 0 출력
def b():
# global로 지정하면, global의 변수에 대입하는 것이 가능하다.
global spam
spam = 1
b()
print(spam) # 1 출력
- 변수를 사용할 때, scope를 지정하는 것으로 그 Scope내 부터 변수를 검색하는 것이 가능하다.
spam = 0
def a():
spam = 1
def b():
# global를 지정하는 것으로 보통 우선 순위가 높은 nonlocal이 아닌 global의 변수를 사용하는 것이 가능하다.
global spam
return spam
return b()
print(a()) # 0 출력
- 아래의 코드는 Python의 튜토리얼 코드를 인용한 것으로 이것을 이해했다면 이름공간과 Scope에 대해 이해했다고 할 수 있다.
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignment:",spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:",spam)
scope_test()
print("In global scope:", spam)
"""
[출력 결과]
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
"""
참고자료
https://www.atmarkit.co.jp/ait/articles/1612/09/news030.html
'IT > 언어' 카테고리의 다른 글
[python] python의 Comprehension (0) | 2020.08.26 |
---|---|
[python] python 인스턴스 변수와 클래스 변수 (0) | 2020.08.23 |
[python] python의 함수 인수(인자) 정리 (0) | 2020.08.19 |
[python] python의 변수 사용 범위 정리 (1) (0) | 2020.08.18 |
[python] python의 sort함수에 사용되는 lambda에 대한 이해 (0) | 2020.08.17 |