WeniVooks

검색

견고한 파이썬

지역 변수와 전역 변수

1. 지역 변수

지역 변수는 함수 내부에서만 정의되고 사용되는 변수입니다. 이를 통해 다른 함수나 프로그램의 전체 흐름에 영향을 주지 않고, 함수 내부에서만 데이터를 관리할 수 있습니다.

이는 복잡한 프로그램에서 여러 개발자가 작업할 때 변수 이름 충돌을 방지하고, 코드의 유지 보수를 용이하게 합니다.

# 지역 변수 사용 예시 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 + 1은 연산자 우선순위에 의해 a + 1이 먼저 실행됩니다. 그러면 a는 100으로 전역변수에 있는 값이 되는 것이죠. 그런데 a에 할당하려니 전역변수는 수정할 수 없습니다. 따라서 a를 지역변수로 놓고 a + 1을 하려니 지역변수에는 a가 선언된 적이 없습니다. 따라서 a + 1 연산을 할 수 없기 때문에 local variable 'a' referenced before assignment에러가 출력됩니다. 로컬 변수인 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>
}
{
  '__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)

:::{.callout}

nonlocal 키워드는 13-10 장에서 다룹니다. 여기서는 지역변수, 전역변수에 대해서만 다룹니다.

:::

{"packages":["numpy","pandas","matplotlib","lxml"]}
7.3 함수의 아규먼트와 파라미터7.5 함수에서 pass 사용하기