지역 변수와 전역 변수
1. 지역 변수
지역 변수는 함수 내부에서만 유효하고, 전역 변수는 프로그램 전체에서 접근 가능합니다. 전역 변수 수정 시 'global' 키워드가 필요하지만, 가능하면 함수의 인자와 반환값을 활용하는 것이 좋습니다.지역 변수는 함수 내부에서만 정의되고 사용되는 변수입니다. 이를 통해 다른 함수나 프로그램의 전체 흐름에 영향을 주지 않고, 함수 내부에서만 데이터를 관리할 수 있습니다.
이는 복잡한 프로그램에서 여러 개발자가 작업할 때 변수 이름 충돌을 방지하고, 코드의 유지 보수를 용이하게 합니다.
# 지역 변수 사용 예시
def f():
a = 1
print(a) # 출력: 1
f()
# print(a) # error: a는 f 함수 내부에서만 정의되었습니다.
위 코드에서 a 변수는 지역변수입니다. 따라서 밖에서 이 변수에는 접근할 수 없습니다.
지역에 선언된 변수 목록을 보려면 locals()를 사용하면 됩니다.
def f():
a = 1
print(locals()) # 출력: {'a': 1}
f()
함수 내부에서 함수를 선언하여 locals()로 어떻게 할당이 되었는지 확인해보겠습니다.
def f():
a = 1
b = 'hello'
def ff():
pass
print(locals()) # 출력: {'a': 1, 'b': 'hello', 'ff': <function f.<locals>.ff at 0x7a46e99d2c20>}
f()
위처럼 ‘ff’도 변수명으로 등록이 된 것을 확인할 수 있습니다.
2. 전역 변수
전역 변수는 프로그램 전체, 어떤 함수에서도 접근 가능한 변수입니다. 함수 내부에서 함수 외부에 정의된 변수를 변경하려면, 그 변수를 global로 선언해야 합니다. 키워드를 사용하면 함수 내부에서 전역 변수를 참조하고 수정할 수 있게 됩니다.
그러나 global 키워드를 사용한 전역 변수의 변경은 권장되지 않습니다. 이는 코드가 복잡해질 때 변수의 값이 예기치 않게 변경될 수 있어 버그를 유발할 위험이 있기 때문입니다. 대신, 필요한 값을 함수의 인자로 전달하고, 함수의 반환 값을 사용하여 결과를 얻는 방식을 선호합니다. 이 방법은 객체 지향 프로그래밍에서 권장하는 방식으로, 코드의 안정성과 유지보수성을 높여줍니다.
이렇게 하면, 각 함수는 주어진 입력에 대해서만 작동하고 외부 변수에 의존하지 않으므로, 함수의 행동이 예측 가능해지며, 다른 코드 부분과의 상호 작용에서 발생할 수 있는 문제를 줄일 수 있습니다.
예제를 살펴보도록 하겠습니다.
a = 100
def f():
global a
a = a + 1
f()
print(a) # 출력: 101, 함수 f 내에서 전역 변수 a가 수정되었습니다.
이번에는 아래와 같이 global을 지우고 실행해보도록 하겠습니다.
a = 100
def f():
a = a + 1 # error
f()
print(a)
함수 안에서 전역 변수를 global 키워드 없이 수정하는 것은 허락하지 않기 때문에 에러가 납니다.
이번에는 전역 변수를 읽기만 해보도록 하겠습니다.
a = 100
def f():
print(a) # 출력: 100
f()
def ff():
b = a + 1
print(b) # 출력: 101
ff()
위처럼 불러와서 읽기만 하는 경우에는 사용이 가능합니다. 이처럼 global 변수를 쓰지 않더라도 외부 변수에 접근하는 것은 가능합니다. 변경이 불가능한 것이죠. 다음 예시도 한 번 보도록 하겠습니다.
a = 100
def f():
a = 1000
print(a) # 출력: 1000
f()
위 예시는 error가 출력이 될 것 같지만 1000이 출력이 됩니다. 이유는 전역에 있는 a와 로컬에 있는 a변수가 구분되기 때문입니다. 두 변수는 다른 변수인 것입니다. 그러면 에러가 났던 코드를 다시 가져와 살펴보도록 하겠습니다.
a = 100
def f():
a = a + 1 # error
f()
파이썬은 함수 본문을 미리 훑어보고 어딘가에서 한 번이라도 값을 할당받는 이름은 그 함수의 지역 변수로 확정합니다. 위 함수에는 a = ...라는 할당이 있으므로, a는 이 함수 안에서 처음부터 끝까지 지역 변수입니다.
그런데 실행 순서상 a + 1이 먼저 계산되어야 합니다. 이때 파이썬은 전역에 있는 100을 가져오지 않고, 지역 변수 a를 찾습니다. 아직 아무 값도 담기지 않았기 때문에 local variable 'a' referenced before assignment 에러가 납니다. 로컬 변수인 a가 할당되기 전에 참조되었다는 것이죠.
바로 위 예제에서 print(a)만 있을 때는 정상 동작했던 것과 비교해보세요. 그 함수에는 a에 대한 할당이 없었기 때문에 a가 지역 변수로 확정되지 않았고, 그래서 전역의 값을 읽어올 수 있었습니다.
전역에 정의된 변수는 globals()를 사용하여 출력 가능합니다. 다만 전역에는 좀 더 많은 변수들이 선언되어 있습니다. 우리가 선언한 변수는 마지막 부분에 있습니다.
a = 1
b = 'hello'
def f():
pass
print(globals())
{
'__name__': '__main__',
'__doc__': 'Automatically created module for IPython interactive environment',
# ...생략...
'a': 1,
'b': 'hello',
'f': <function f at 0x7b64af2453f0>
}
3. 나아가기
해당 예제는 여러 개의 중첩 함수를 정의하고 호출하면서 지역 변수 a의 스코프를 보여줍니다. 각 함수(f, ff, fff) 내에서 a라는 이름의 지역 변수가 따로 생성되고, 해당 함수의 스코프 내에서만 그 변수가 유효합니다.
a = 10
def f():
a = 100
print(a) # 출력: 100
def ff():
a = 1000
print(a) # 출력: 1000
def fff():
a = 10000 # 출력: 10000
print(a)
fff()
ff()
f()
print(a) # 출력: 10
해당 예제는 전역 변수와 지역 변수, 그리고 global 키워드의 사용을 보여주는 복잡한 예입니다. 여러 단계의 중첩 함수를 통해 변수 a의 스코프를 다양하게 변경하고 있습니다.
# global을 하면 함수가 얼만큼 중첩되어 있건 최상단에 있는 a를 바라봅니다.
a = 10
def f():
a = 100
print(a)
def ff():
a = 1000
print(a)
def fff():
global a
print(a)
fff()
ff()
f()
print(a)
nonlocal 키워드는 '함수 심화' 챕터의 nonlocal 장에서 다룹니다. 여기서는 지역변수, 전역변수에 대해서만 다룹니다.