Python namespace란 무엇이고 왜 필요할까?
1. Python name?
Python에는 모든 것이 객체로 표현되지만 이 객체들 데이터 타입으로 나눠보면 문자, 숫자, 불리언(Boolean), 리스트 등이 있습니다.
이 모든 것들에 대해 이름을 지어줄 수 있습니다.
예를 들면 아래처럼 문자, 숫자, 불리언, 리스트를 각각
my_string, my_number, my_boolean, my_list처럼 명명할 수 있습니다.
my_string = 'spam and eggs'
my_number = 42
my_boolean = True
my_list = ['spam', 'eggs']
마찬가지로 지난글(2020/04/09 - [Python/Basic] - Python First Class Function 일급 함수)에서 언급한 것처럼
함수도 객체이므로 위 데이터 타입들처럼 명명할 수 있습니다.
def spam():
return 'SPAM SPAM SPAM'
eggs = spam
print(spam()) # SPAM SPAM SPAM
print(eggs()) # SPAM SPAM SPAM
eggs = spam 부분을 보면 함수를 호출 할 때와 다르게 괄호를 사용하지 않았는데 이는 함수를 eggs에 할당했다는 의미입니다. 이 차이를 아는 것이 중요합니다.
def spam():
return 'SPAM SPAM SPAM'
print(spam) # <function spam at 0x123456789>
print(spam()) # SPAM SPAM SPAM
다시 정리하자면 Python에서는 모든 것이 객체이고 이에 이름을 붙여서 사용할 수 있습니다.
2. Python namespace란?
Python에서 namespace란 name들의 공간입니다.
공식 파이썬 튜토리얼 문서를 보면 namespace를 다음과 같이 정의합니다.
namespace란 이름(변수명)과 객체를 연결한 것
아래 예시에서 spam, eggs이라는 이름(변수)을 할당했다고 이들 자체가 '문자'는 아닙니다.
이름은 단순히 객체(문자)를 가리킨다고 봐야합니다.
그래서 같은 객체를 다른 변수명으로 참조해도 문제가 되지 않습니다.
다만 같은 namespace에 존재하는 name들이 같은 객체를 가리키고 있는 것입니다.
spam = 'spam and eggs'
eggs = spam
print(spam) # spam and eggs
print(eggs) # spam and eggs
print(id(spam)) # 4476916512
print(id(eggs)) # 4476916512
3. 캡슐화와 스코핑
객체지향프로그래밍에서는 흔히 캡슐화라는 것이 존재합니다.
변수, 함수들을 한 클래스에 담기는 것인데 Python에서 캡슐화라고 한다면 namespace와 scope를 빼놓고 이야기하기 어렵습니다.
간단하게 생각해서 Python module이라고 하면 독자적인 namespace를 가진다고 생각해보겠습니다.
그런 의미에서 한 Python module에서 같은 이름을 가진 함수를 만들 수 없습니다. 하지만 namespace를 따로 갖고 있는 다른 Python module을 만든다면 같은 이름의 함수를 만들어도 상관 없습니다. namespace가 분리되어 있기 때문입니다.
비슷하게 같은 Python module 내에서라도 함수는 자신만의 namespace를 가집니다.
def spam():
eggs = 'spam and eggs'
print(eggs)
spam() # spam and eggs
print(eggs) # raises a NameError exception
spam 함수 안에서는 eggs가 참조 되지만 spam 함수를 벗어난 밖에서는 eggs라는 이름을 찾을 수 없습니다.
eggs는 spam 함수만에 namespace에서만 존재하고 그 안에서만 사용될 수 있기 대문입니다.
spam 함수 안에 namespace를 local space라고 생각하고 그 밖을 global space라고 보면
위에 예에서는 global space에서 local space에 존재하는 eggs를 찾기 때문에 에러가 발생했습니다.
하지만 그 반대는 그렇지 않습니다. 무슨 말이냐면
global space에서 local space에 접근하는 것은 불가했지만
local space에서 global space에 접근하는 것은 가능합니다.
예를 들면 spam에서 eggs를 밖으로 빼놨습니다. global space로 eggs를 이동시키고 spam함수 내에서 eggs를 참조하지만 아무런 문제가 없습니다.
local space에서 global space에 접근하는 것은 가능하기 때문입니다.
def spam():
print(eggs)
eggs = 'spam and eggs'
spam() # spam and eggs
같은 원리지만 class에서는 조금 헤깔릴 수 있습니다.
class Meal:
def __init__(self):
self.eggs = 2
my_meal = Meal()
print(my_meal.eggs) # 2
print(eggs) # raises a NameError exception
eggs를 접근할 때 my_meal.eggs는 되지만 그냥 eggs는 접근할 수 없습니다.
print(eggs)라고 하면 global space에서 eggs라는 변수가 있나 찾아보지만 eggs는 없기 때문입니다.
4. LEGB
local space에서는 global space를 참조했지만 반대는 그렇지 않듯이 namespace끼리의 의존관계가 있습니다.
이를 LEGB라고 표현하는데 L > E > G > B 이렇게 참조가 가능합니다.
하나씩 살펴보면,
Local – 함수 내에서 할당된 이름
Enclosing – 클로져(함수 내 함수)에 할당된 이름
Global – top-level module (Python file의 시작점)에 할당된 이름
Built-in – Python 내장 영역에 할당된 이름(open, import, print return, Exception 등)
위에서 알 수 있는 사실은
- 가장 낮은 레벨은 Local 영역, 가장 높은 레벨은 Built-in 영역입니다.
- 낮은 레벨의 영역은 위 레벨 영역을 참조할 수 있습니다.
- 그 반대는 안됩니다. Global에서 Local을 참조할 수 없듯이 Built-in에서는 아무 영역도 접근할 수 없습니다.
위 예시를 색깔별로 표현해보면 이렇습니다.
5. Imports
Python module는 각각의 Global namespace를 가지고 있습니다. 그래서 한 module 내에서는 같은 이름을 가진 함수를 만들 수 없었지만 다른 module에서는 같은 이름을 지어도 문제되지 않습니다.
다른 module을 import한다는 것은 namespace끼리의 참조가 생기는 것입니다.
예를 들어,
spam.py
my_string = 'spam and eggs'
my_number = 42
main.py
import spam
print(spam.my_string) # spam with eggs
print(spam.my_number) # 42
main.py에서 import spam을 하면 spam.py 내 존재하는 my_string, my_number에 접근할 수 있지만
my_string, my_number처럼 사용할 수는 없습니다.
main.py의 Global space에서는 spam이라는 모듈로 할당된 것이기 때문에 spam.my_string, spam.my_number처럼만 사용할 수 있습니다.
물론 spam.py에서 특정한 이름만 가져올 수도 있습니다.
main.py
from spam import my_string
print(my_string) # spam with eggs
print(my_number) # raises a NameError
spam에서 my_string만 가져오면 my_number에는 접근할 수 없습니다.
이렇게 특정한 이름만 가져올 경우에는 (from xx import xx) 바로 이름에 접근할 수 있습니다.
From spam import *
Global namespace에 바로 이름을 가져올 수 있는 방법도 있습니다.
From spam import *라고 하면 spam에 있는 모든 이름을 Global space에 가져오는 방법으로
위에서 spam.my_string, spam.my_number처럼 접근하지 않고 바로 이름에 접근할 수 있습니다.
from spam import *
print(my_string) # spam with eggs
print(my_number) # 42
기본적으로 From spam import *을 통해 spam에 존재하는 모든 이름들을 가져올 수 있지만
이를 제어할 수 있는 방법도 있습니다.
spam.py
__all__ = (
'my_string',
)
my_string = 'spam and eggs'
my_number = 42
__all__ 안에 my_string만 둔다면 다른 module에서 spam을 import할 경우 my_string만 할당시킬 수 있습니다.
spam.py를 이렇게 만들어 두고 main.py를 보겠습니다.
main.py
from spam import *
print(my_string) # spam with eggs
print(my_number) # raises a NameError
이미지 및 영문의 원글 출처 : https://blog.confirm.ch/python-namespaces/
같이 읽어보면 좋은 글
2022.12.27 - [파이썬/가상화폐] - [전자책] 바이낸스 코인선물자동매매 시스템 개발 방법을 담은 책이 출시되었습니다.
2022.11.05 - [파이썬/가상화폐] - [공지] 코인거래소별 프리미엄 체크봇 개발 가이드와 풀소스 전자책 | binance bybit | 업비트 김치프리미엄
2022.10.19 - [부업] - 비전공자를 위한 Python 기초책, 읽다보면 알게되는 파이썬 전자책!
'파이썬 > 기초문법' 카테고리의 다른 글
Python 인터프리터 vs 컴파일러 / Is Python interpreted or compiled? Yes 번역 (0) | 2020.04.20 |
---|---|
Python if __name__ == '__main__': 에 대한 이해 / Python __name__ (0) | 2020.04.18 |
Python First Class Function 일급 함수 (0) | 2020.04.09 |
Python Mutable vs Immutable / Mutable / Immutable (0) | 2020.03.16 |
Python 클래스에 대해 1 (0) | 2020.03.15 |
댓글