Python에서 변수를 사용할 때는 단순히 변수에 값을 저장하는 것만큼이나 그 변수를 어디에서 사용할 수 있는지를 이해하는 것도 중요합니다.
같은 이름의 변수라도 함수 밖에서 만들어졌는지, 함수 안에서 만들어졌는지에 따라 서로 다른 변수로 동작할 수 있습니다.
이처럼 변수가 사용할 수 있는 범위를 스코프(Scope), 즉 변수의 유효 범위라고 합니다.
Python에서는 크게 전역 변수와 지역 변수로 구분해서 이해하면 됩니다.
전역 변수란?
함수 바깥에서 만들어진 변수를 전역 변수(Global Variable)라고 합니다.
name = "Python"
def greet():
print(name)
greet()
결과:
Python
name은 함수 밖에서 만들어졌기 때문에 전역 변수입니다.
그리고 greet() 함수 안에서도 name을 사용할 수 있습니다.
즉, 함수 내부에서 변수를 찾았을 때 해당 함수 안에 변수가 없다면 바깥쪽에서 해당 이름을 찾을 수 있습니다.
지역 변수란?
함수 안에서 만들어진 변수를 지역 변수(Local Variable)라고 합니다.
def greet():
message = "안녕하세요"
print(message)
greet()
여기서 message는 greet() 함수 내부에서 만들어졌습니다.
따라서 message는 지역 변수입니다.
함수 밖에서 다음과 같이 사용하려고 하면 문제가 발생합니다.
def greet():
message = "안녕하세요"
greet()
print(message)
message는 greet() 함수 내부에서 만들어진 지역 변수이기 때문에 함수 밖에서는 사용할 수 없습니다.
즉,
함수 밖
↓
전역 변수
함수 안
↓
지역 변수
라고 생각하면 기본적인 개념을 이해하기 쉽습니다.
같은 이름의 변수를 사용할 수 있다
전역 변수와 지역 변수는 같은 이름을 사용할 수도 있습니다.
name = "전역 변수"
def show_name():
name = "지역 변수"
print(name)
show_name()
print(name)
결과:
지역 변수
전역 변수
함수 안에서는 지역 변수인 name이 사용되고, 함수 밖에서는 전역 변수인 name이 사용됩니다.
서로 이름은 같지만 서로 다른 범위에 존재하는 변수입니다.
함수 안에서 변수를 만들면 지역 변수가 된다
다음 코드를 살펴보겠습니다.
number = 10
def change_number():
number = 20
print(number)
change_number()
print(number)
결과:
20
10
함수 안에서 number = 20을 작성했다고 해서 전역 변수 number가 변경된 것이 아닙니다.
함수 내부에서 새로운 지역 변수 number를 만든 것입니다.
따라서 함수 실행이 끝난 뒤 전역 변수 number에는 여전히 10이 저장되어 있습니다.
이 부분은 Python에서 자주 헷갈리는 내용입니다.
전역 변수의 값을 함수에서 읽을 수 있다
전역 변수는 함수 내부에서 읽을 수 있습니다.
count = 10
def show_count():
print(count)
show_count()
결과:
10
함수 내부에 count라는 지역 변수가 없기 때문에 Python은 바깥쪽에서 count를 찾습니다.
따라서 전역 변수의 값을 읽을 수 있습니다.
전역 변수의 값을 직접 변경하려면?
전역 변수의 값을 함수 내부에서 변경하려고 하면 주의해야 합니다.
다음 코드를 보겠습니다.
count = 10
def increase():
count = count + 1
print(count)
increase()
이 코드는 오류가 발생합니다.
함수 안에서 count에 값을 할당하고 있기 때문에 Python은 count를 지역 변수로 판단합니다.
그런데 오른쪽에서도 같은 count를 사용하려고 하기 때문에 문제가 발생합니다.
전역 변수 count를 함수 안에서 직접 변경하려면 global 키워드를 사용할 수 있습니다.
count = 10
def increase():
global count
count = count + 1
increase()
print(count)
결과:
11
global count라고 선언하면 함수 내부의 count가 전역 변수 count를 가리키도록 할 수 있습니다.
global 키워드는 신중하게 사용하는 것이 좋다
global을 사용하면 전역 변수의 값을 함수에서 변경할 수 있지만, 너무 많이 사용하는 것은 피하는 것이 좋습니다.
예를 들어 여러 함수가 하나의 전역 변수를 계속 변경하면 어느 함수에서 값이 변경되었는지 추적하기 어려워질 수 있습니다.
count = 0
def increase():
global count
count += 1
def decrease():
global count
count -= 1
이런 구조에서는 프로그램이 커질수록 count의 상태가 여러 함수에 의해 변경될 수 있습니다.
따라서 단순한 예제에서는 global을 사용할 수 있지만, 실제 프로그램에서는 함수의 인자와 반환값을 이용해 데이터를 주고받는 방식이 더 명확한 경우가 많습니다.
예를 들어 다음과 같이 작성할 수 있습니다.
def increase(count):
return count + 1
count = 0
count = increase(count)
print(count)
이 방식은 함수가 어떤 값을 받아서 어떤 값을 반환하는지 명확하게 확인할 수 있습니다.
지역 변수는 함수가 끝나면 사용할 수 없다
지역 변수는 일반적으로 해당 함수가 실행되는 동안 사용할 수 있습니다.
def create_message():
message = "Hello"
print(message)
create_message()
함수 내부에서는 message를 사용할 수 있습니다.
하지만 함수 밖에서는 직접 사용할 수 없습니다.
print(message)
함수의 지역 변수와 전역 변수를 구분하는 것이 중요한 이유입니다.
Python의 변수 탐색 순서: LEGB
Python은 변수를 찾을 때 일정한 순서로 이름을 탐색합니다.
이를 LEGB 규칙이라고 합니다.
L → Local
E → Enclosing
G → Global
B → Built-in
각각의 의미는 다음과 같습니다.
| 범위 | 의미 | 예시 |
|---|---|---|
| Local | 현재 함수 내부 | 함수 안에서 만든 변수 |
| Enclosing | 바깥쪽 함수 | 중첩 함수에서 외부 함수의 변수 |
| Global | 모듈 전체 | 함수 밖에서 만든 변수 |
| Built-in | Python 기본 영역 | len(), print() 등 |
간단한 코드로 보면 다음과 같습니다.
name = "global"
def outer():
name = "enclosing"
def inner():
name = "local"
print(name)
inner()
outer()
결과:
local
inner() 안에 name이 있기 때문에 가장 가까운 지역 변수인 "local"을 사용합니다.
지역 변수가 없으면 바깥쪽을 찾는다
다음과 같이 지역 변수 name을 제거해보겠습니다.
name = "global"
def outer():
name = "enclosing"
def inner():
print(name)
inner()
outer()
결과:
enclosing
inner() 안에 name이 없기 때문에 바깥쪽 함수인 outer()의 name을 사용합니다.
이것이 LEGB의 E, 즉 Enclosing 영역입니다.
전역 변수까지 찾아간다
이번에는 outer()에서도 name을 제거해보겠습니다.
name = "global"
def outer():
def inner():
print(name)
inner()
outer()
결과:
global
inner()에도 없고 outer()에도 없기 때문에 전역 영역에서 name을 찾습니다.
이처럼 Python은 가까운 범위부터 바깥쪽으로 이동하면서 변수를 찾습니다.
Built-in 영역도 있다
마지막으로 Built-in 영역이 있습니다.
Python에서 기본적으로 제공되는 print(), len(), sum() 등의 함수가 여기에 해당합니다.
예를 들어 별도로 len()을 정의하지 않아도 사용할 수 있습니다.
numbers = [10, 20, 30]
print(len(numbers))
결과:
3
len을 현재 지역이나 전역 영역에서 찾지 못하면 Python은 Built-in 영역에서 찾습니다.
변수 이름을 Built-in 함수와 같게 만들면?
전역 변수의 이름을 len으로 만들어보겠습니다.
len = 10
print(len)
이 코드는 정상적으로 실행됩니다.
하지만 이제 len()을 사용하려고 하면 문제가 발생합니다.
numbers = [1, 2, 3]
print(len(numbers))
len이라는 이름이 이미 전역에서 10이라는 값을 가리키고 있기 때문입니다.
따라서 다음과 같은 Python 기본 함수 이름을 변수 이름으로 사용하는 것은 피하는 것이 좋습니다.
list
str
int
sum
len
max
min
변수 이름을 정할 때는 Python의 기본 함수나 타입 이름을 덮어쓰지 않도록 주의하는 것이 좋습니다.
지역 변수와 전역 변수의 차이
지금까지의 내용을 표로 정리하면 다음과 같습니다.
| 구분 | 전역 변수 | 지역 변수 |
|---|---|---|
| 선언 위치 | 함수 밖 | 함수 안 |
| 사용 범위 | 프로그램의 해당 모듈 전역 | 해당 함수 내부 |
| 함수에서 읽기 | 가능 | 가능 |
| 함수에서 직접 변경 | global 필요 | 가능 |
| 함수 실행 종료 후 | 계속 존재 | 해당 지역에서는 사용할 수 없음 |
가장 중요한 차이는 어디에서 만들어졌는가입니다.
전역 변수와 지역 변수를 함께 사용할 때
실제 코드에서는 전역 변수와 지역 변수가 함께 존재할 수 있습니다.
message = "전역"
def show_message():
message = "지역"
print(message)
show_message()
print(message)
결과:
지역
전역
이런 상황에서는 현재 코드가 어느 범위의 변수를 사용하는지 확인해야 합니다.
특히 같은 이름의 변수를 여러 범위에서 사용하는 경우 코드가 복잡해질 수 있으므로 변수 이름을 명확하게 짓는 것도 중요합니다.
함수의 인자도 지역 변수다
함수의 매개변수 역시 함수 내부에서는 지역 변수처럼 동작합니다.
def greet(name):
print(name)
greet("Python")
여기서 name은 greet() 함수 내부에서 사용할 수 있는 지역적인 이름입니다.
따라서 다음과 같이 함수 밖에서 name을 사용할 수 있는 것은 아닙니다.
def greet(name):
print(name)
greet("Python")
print(name)
함수의 매개변수도 해당 함수의 지역 범위에 속한다는 점을 기억하면 좋습니다.
전역 변수보다 함수의 인자와 반환값을 우선하기
전역 변수는 편리하지만 프로그램이 커질수록 관리하기 어려워질 수 있습니다.
다음과 같이 전역 변수를 직접 변경하는 방식보다는,
total = 0
def add(value):
global total
total += value
함수의 인자와 반환값을 사용하는 방식이 더 명확한 경우가 많습니다.
def add(total, value):
return total + value
total = 0
total = add(total, 10)
total = add(total, 20)
print(total)
결과:
30
이 방식은 함수가 어떤 데이터를 입력받고 어떤 결과를 반환하는지 코드만 보고도 파악하기 쉽습니다.
마무리
Python에서 변수의 스코프를 이해하면 함수가 데이터를 어떻게 사용하는지 훨씬 쉽게 파악할 수 있습니다.
핵심 내용을 정리하면 다음과 같습니다.
- 함수 밖에서 만든 변수는 전역 변수입니다.
- 함수 안에서 만든 변수는 지역 변수입니다.
- 함수 내부에서는 전역 변수의 값을 읽을 수 있습니다.
- 함수 내부에서 같은 이름으로 값을 할당하면 지역 변수로 취급됩니다.
- 전역 변수를 함수에서 직접 변경하려면
global을 사용할 수 있습니다. - Python은 LEGB 순서에 따라 변수를 탐색합니다.
- 함수의 매개변수 역시 함수 내부의 지역적인 이름입니다.
- 전역 변수는 편리하지만 가능한 경우 함수의 인자와 반환값을 사용하는 것이 코드를 관리하기 쉽습니다.
이번 글을 마지막으로 함수 기초 문법부터 디폴트 파라미터, 패킹과 언패킹, 함수 객체, 일급 객체, 람다, 변수의 스코프까지 함수와 관련된 핵심 개념을 한 단계씩 살펴봤습니다.