35.2 함수 안에서 함수 만들기

이번에는 함수 안에서 함수를 만드는 방법을 알아보겠습니다. 다음과 같이 def로 함수를 만들고 다시 def로 함수를 만들면 됩니다.

def 함수이름1():
    코드
    def 함수이름2():
        코드

그럼 간단하게 함수 안에서 문자열을 출력하는 함수를 만들고 호출해보겠습니다.

function_in_function.py

def print_hello():
    hello = 'Hello, world!'
    def print_message():
        print(hello)
    print_message()
 
print_hello()

실행 결과

Hello, world!

함수 print_hello 안에서 다시 def로 함수 print_message를 만들었습니다. 그리고 print_hello 안에서 print_message()처럼 함수를 호출했습니다. 하지만 아직 함수를 정의만 한 상태이므로 아무것도 출력되지 않습니다.

두 함수가 실제로 동작하려면 바깥쪽에 있는 print_hello를 호출해주어야 합니다. 즉, print_hello > print_message 순으로 실행됩니다.

이제 지역 변수의 범위를 살펴보겠습니다. 안쪽 함수 print_message에서는 바깥 함수의 지역 변수 hello를 사용할 수 있습니다.

def print_hello():
    hello = 'Hello, world!'
    def print_message():
        print(hello)    # 바깥 함수의 지역 변수를 사용

지역 변수의 접근 범위를 그림으로 나타내면 다음과 같은 모양이 됩니다. 반대로 말하면 함수의 지역 변수는 그 안에 만든 모든 함수에서 접근할 수 있습니다.

그림 35-3 함수의 지역 변수에 접근할 수 있는 범위
그림 35 3 함수의 지역 변수에 접근할 수 있는 범위

이제 바깥 함수의 지역 변수를 변경해보겠습니다. 다음은 함수 add에서 두 값을 더해서 함수 calc의 지역 변수 total에 누적합니다.

function_local_error.py

def calc():
    total = 0    # calc의 지역 변수
    def add(a, b):
        total = total + a + b    # add의 지역 변수 total을 사용하려고 해서 에러 발생
 
    add(10, 20)
    add(30, 40)
    print(total)
 
calc()

실행 결과

Traceback (most recent call last):
  File "C:₩project₩function_local_error.py", line 10, in <module>
    calc()
  File "C:₩project₩function_local_error.py", line 6, in calc
    add(10, 20)
  File "C:₩project₩function_local_error.py", line 4, in add
    total = total + a + b
UnboundLocalError: local variable 'total' referenced before assignment

실행을 해보면 지역 변수 total이 할당되기 전에 참조되었다는 에러가 발생합니다. 겉으로 보기에는 함수 calc의 지역 변수 total에 접근한 것 같지만 실제로는 함수 add 안에서 이름이 같은 지역 변수 total 사용하려다가 에러가 발생합니다. 파이썬에서는 함수 안에서 변수를 만들면 항상 현재 함수의 지역 변수가 됩니다. 여기서는 add 안에서 total에 값을 할당하지 않아서 add의 지역 변수가 없는 상태입니다.

def calc():
    total = 0    # calc의 지역 변수
    def add(a, b):
        total = total + a + b    # add의 지역 변수 total을 사용하려고 해서 에러 발생

함수 바깥에 있는 지역 변수의 값을 변경하려면 nonlocal 키워드를 사용해야 합니다. 다음과 같이 함수 안에서 nonlocal에 지역 변수의 이름을 지정해줍니다.

  • nonlocal 지역변수

function_nonlocal_keyword.py

def calc():
    total = 0
    def add(a, b):
        nonlocal total    # 바깥 함수의 지역 변수 total을 사용하겠다고 설정
        total = total + a + b
 
    add(10, 20)
    add(30, 40)
    print(total)
 
calc()

실행 결과

100

이제 함수 add에서 함수 calc의 지역 변수 total에 접근할 수 있습니다. 즉, nonlocal은 현재 함수의 지역 변수가 아니라는 뜻이며 바깥 함수의 지역 변수를 사용합니다.

물론 매개변수도 함수 안에서만 사용할 수 있는 지역 변수이므로 nonlocal을 사용할 수 있습니다.

def calc(total):
    def add(a, b):
        nonlocal total    # 바깥 함수의 매개변수 total을 사용하겠다고 설정
        total = total + a + b

nonlocal은 함수 바깥의 지역 변수를 찾을 때 가까운 함수부터 먼저 찾습니다. 이번에는 함수의 단계를 간단하게 표현하기 위해 함수 이름을 A, B, C로 만들겠습니다.

function_in_function_nonlocal_keyword.py

def A():
    x = 10
    y = 100
    def B():
        x = 20
        def C():
            nonlocal x
            nonlocal y
            x = x + 30
            y = y + 300
            print(x)
            print(y)
        C()
    B()
 
A()

실행 결과

50
400

함수 C에서 nonlocal x를 사용하면 바로 바깥의 함수 B의 지역 변수 x = 20을 사용하게 됩니다. 따라서 x = x + 30은 50이 나옵니다. 그리고 함수 C에서 nonlocal y를 사용하면 바깥 함수의 지역 변수 y를 사용해야 하는데 함수 B에는 y가 없습니다. 이때는 한 단계 더 바깥으로 나가서 함수 A의 지역 변수 y를 사용하게 됩니다. 즉, 가까운 함수부터 지역 변수를 찾고, 지역 변수가 없으면 계속 바깥으로 나가서 찾습니다.

실무에서는 이렇게 여러 단계로 함수를 만들 일은 거의 없습니다. 그리고 함수마다 이름이 같은 변수를 사용하기 보다는 변수 이름을 다르게 짓는 것이 좋습니다.

참고로 함수가 몇 단계든 상관없이 global 키워드를 사용하면 무조건 전역 변수를 사용하게 됩니다.

function_in_function_global_keyword.py

x = 1
def A():
    x = 10
    def B():
        x = 20
        def C():
            global x
            x = x + 30
            print(x)
        C()
    B()
 
A()

실행 결과

31

함수 C에서 global x를 사용하면 전역 변수 x = 1을 사용하게 됩니다. 따라서 x = x + 30은 31이 나옵니다.

파이썬에서 global을 제공하지만 함수에서 값을 주고받을 때는 매개변수와 반환값을 사용하는 것이 좋습니다. 특히 전역 변수는 코드가 복잡해졌을 때 변수의 값을 어디서 바꾸는지 알기가 힘듭니다. 따라서 전역 변수는 가급적이면 사용하지 않는 것을 권장합니다.