파이썬을 처음 배우다 보면 다음과 같은 의문이 생길 수 있다:
"서로 다른 파일에서 함수와 변수가 어떻게 서로를 찾아가는 걸까?"
간단한 예제를 통해 이 개념을 명확하게 이해해보자.
아래와 같이 file_a.py와 file_b.py가 있다.
# file_a.py
some_variable = "Hello"
def some_function():
print(some_variable) # 이 some_variable은 어디서 오는가?
# file_b.py
from file_a import some_function
some_function() # 실행하면 "Hello"가 출력됨
단순한 예제이지만, 파이썬의 중요한 동작원리가 숨어있다.
실행 순서 이해하기
python file_b.py 명령을 실행하면 다음과 같은 순서로 코드가 실행된다:
- file_b.py 실행 시작.
- from file_a import some_function 구문을 만남.
- 파이썬은 file_a.py를 찾아서 전체를 실행함.
- some_variable = "Hello" 가 실행됨.
- some_function() 함수가 정의됨.
- some_function을 file_b.py의 namespace로 가져옴.
- file_a의 namespace에 있는 객체 중 오로지 some_function만 가져온 것.
- 만약 모든 것을 가져오려면, from file_a import *를 사용해야 함.
- file_b.py에서 some_function() 호출.
- "Hello"가 출력됨.
Namespace와 Scope
여기서 핵심적인 개념이 바로 namespace와 scope이다.
Namespace
정의: 코드가 실행되는 어떤 특정 시점에 사용 가능한 모든 이름과 그에 매핑된 객체들의 집합. 변수나 함수의 정의, import, 삭제 등의 작업이 발생할 때마다 namespace는 계속 변화한다.
# 시점 1: module이 처음 로드될 때
x = 1
# namespace: {'x': 1}
# 시점 2: 함수가 정의된 후
def add_number(y):
return x + y
# namespace: {'x': 1, 'add_number': <function>}
# 시점 3: 새로운 변수가 추가된 후
z = 2
# namespace: {'x': 1, 'add_number': <function>, 'z': 2}
# 시점 4: 기존 변수가 삭제된 후
del x
# namespace: {'add_number': <function>, 'z': 2}
# 시점 1: 스크립트 시작
# namespace: {}
# 시점 2: math module의 import 후
import math
# namespace: {'math': <module>}
# 시점 3: math에서 특정 함수만 import 후
from math import sin
# namespace: {'math': <module>, 'sin': <function>}
- 각 파이썬 module은 자신만의 namespace를 가진다.
- file_a의 namespace는 some_variable과 some_function의 집합으로 구성된다.
- some_variable은 file_a의 namespace에 존재하는 객체이다.
- 한 module에서 다른 module의 객체를 직접 import해야, namespace에 해당 객체가 비로소 추가된다.
- file_b에서 file_a의 some_variable을 import하므로, file_b의 namespace에 some_variable이 추가된다.
Scope
정의: "변수의 유효 범위" 혹은 "이름이 유효한 범위"를 의미한다. 즉, namespace의 유효 범위를 의미한다.
Scope는 코드 블록에서 변수가 접근 가능한 범위를 정의한다.
LEGB 규칙(Scope의 4단계)
- Local(L): 가장 안쪽의 함수나 클래스 메서드 내부
- Enclosing(E): 중첩 함수에서 바깥쪽 함수의 범위
- Global(G): module 수준의 전역 범위
- Built-in(B): 파이썬 내장 함수와 예약어가 있는 범위
Scope 검색 순서
파이썬에서 변수를 찾을 때는 다음 순서로 검색한다:
- local scope (함수 내부)
- enclosing scope (중첩 함수에서 바깥 함수 범위)
- global scope (Module 레벨)
- built-in scope (파이썬 내장 함수/변수)
따라서 some_function 이 some_variable을 찾는 과정은 다음과 같다:
- local scope 검색: 함수 내부에 없음.
- enclosing scope 검색: 중첩 함수 구조가 아니므로 미해당.
- global scope 검색: file_a의 module 레벨에서 찾아냄.
- built-in scope 검색: global scope에서 찾았으므로 여기까지 오지 않음.
Module import의 특징
파이썬의 module 시스템에는 다음과 같은 중요한 특징이 있다:
- 한 번만 실행:
- Module은 처음 import될 때만 실행된다.
- 같은 module을 여러 번 import해도 한 번만 실행된다.
- 클로저(Closure):
- 함수는 자신이 정의된 환경의 변수들을 기억한다.
(심화) 함수는 "자신이 정의된 환경"의 어디까지 기억하는가?
- 함수는 자신의 enclosing scope의 변수들 중 자신이 사용하는 변수들만 기억한다.
- global scope의 변수는 클로저에 저장되지 않고, 필요할 때 module에서 찾는다.
- built-in scope의 함수/변수들도 저장되지 않고, 필요할 때 찾는다.
# global scope
global_var = "global"
def outer():
outer_var = "outer" # enclosing scope
def inner():
local_var = "local" # local scope
print(local_var) # 1. 당연히 접근 가능
print(outer_var) # 2. outer 함수의 변수 기억
print(global_var) # 3. global 변수도 접근 가능
print(len("test")) # 4. built-in 함수도 사용 가능
return inner # inner 함수를 반환
# inner 함수를 할당
func = outer()
# 나중에 실행해도 outer_var를 기억
func() # 모든 출력이 정상적으로 작동
1. 클로저의 범위
def outer():
outer_var = "remembered" # 이 변수는 클로저에 저장됨
def inner():
print(outer_var)
return inner
func = outer()
print(func.__closure__) # 클로저 정보 확인 가능
2. 실제로 기억하는 것
def outer():
outer_var = "outer"
other_var = "not used" # inner에서 사용하지 않음
def inner():
print(outer_var)
return inner
func = outer()
# outer_var만 클로저에 저장되고
# other_var는 저장되지 않음
3. 기억하지 않는 것
global_var = "global"
def simple_func():
print(global_var)
# global_var는 클로저에 저장되지 않음
# 왜냐하면 module 레벨(global scope)의 변수이기 때문
따라서 앞선 예제의 some_function도 some_variable의 값을 기억하는게 아니라, some_variable이 global scope의 변수이므로 자신이 정의된 module인 file_a.py에서 찾아보는 것이다.
# file_a.py
some_variable = "Hello"
def some_function():
print(some_variable)
# 클로저 확인
print(some_function.__closure__) # None이 출력됨
# 클로저가 None이라는 것은 저장된 변수가 없다는 의미
결론
파이썬의 namespace와 scope는 변수와 함수의 가시성과 생명주기를 결정하는 중요한 개념이다. 이들을 제대로 이해하고 활용하면, 변수 이름 충돌을 방지하고, 코드의 module성을 높이는 데 유용하다.
'Python > 문법' 카테고리의 다른 글
[Python] Module과 Package (0) | 2025.01.18 |
---|---|
[Python] import 문 사용 팁 (0) | 2025.01.13 |
[Python] Unpacking의 다양한 예시 (2) | 2024.12.27 |
[Python] Iterable과 Sequence 자료형 (0) | 2024.12.27 |
[Python] 제너레이터(Generator) (1) | 2024.12.14 |