“분쇄된 커피 원두를 에스프레소 추출 기계에 넣은 다음에, 기계에 컵을 받치고 추출 버튼을 눌러 줘.”

누군가에게 에스프레소 추출을 부탁할 때마다 이렇게 요구해야 한다면 불편할 것이다. 보통 사람들끼리는 ‘에스프레소 추출 과정’을 약속해 두고 ‘에스프레소 추출해 줘’라고 간단히 말한다. 프로그래밍에서는 함수를 이용해 실행 과정을 약속할 수 있다. 함수를 정의하는 것은 에스프레소 추출 과정을 약속하는 것과 마찬가지다. 자주 사용되는 코드를 함수로 정의해 두면, 그 뒤로는 동일한 코드를 함수 이름만으로 실행시킬 수 있다. 파이썬에서 함수를 정의하는 방법을 알아 보자.

3.3.1 함수를 정의하는 def 문

파이썬에서 함수를 정의할 때는 def 문을 사용한다. def는 ‘정의하다’라는 뜻의 영어 단어 define에서 앞 글자를 딴 것이다. def 문은 다음과 같은 양식으로 작성한다. (# 뒤의 주석은 설명을 위한 것이며 양식에 포함되지 않는다.)

def 함수이름():       # ❶ 헤더 행
    함수의 내용       # ❷ 내용 행 (여러 행)

def 문은 ❶ 헤더 행으로 시작하고, 그 다음 행부터 뒤따르는 여러 행의 ❷ 내용 행으로 구성된다.

def 예약어로 시작하는 헤더 행에는 함수의 이름을 쓴다. 함수의 이름은 변수와 마찬가지로 여러분이 원하는 대로 지을 수 있다. 단, 변수 이름을 지을 때처럼 식별자 규칙(2.2.4 변수는 이름이다)을 따라야 한다. 함수가 하는 일을 직관적으로 알 수 있도록 의미 있는 이름을 짓자.

함수 이름 뒤에는 괄호가 붙고, 괄호 속에는 매개변수가 들어간다. 매개변수는 함수에 값을 전달하기 위한 것이다. 매개변수에 관해서는 조금 뒤에 자세히 알아볼 것이다.

함수의 내용은 함수를 통해 실행할 파이썬 코드다. 원하는 만큼 여러 행의 코드를 작성할 수 있다. 내용 행을 작성할 때는 각 행의 앞마다 공백 네 칸을 띄워야 한다. 이것을 ‘들여쓰기’라고 한다. 들여쓰기는 코드의 블록(구역)을 형성한다. 나란히 들여쓰기된 코드 블록은 헤더 행에서부터 시작된 함수의 내용임을 나타낸다. 들여쓰기가 끝나면, 그 함수의 정의가 끝난다.

def 문으로 함수 정의하기

그러면 def 문을 실제로 작성해 함수를 정의해 보자. 처음 만들어 볼 함수는 사용자로부터 이름을 입력받고, 인사를 출력하는 함수다. 함수를 이용한다는 점을 제외하면, 1장에서 만들어 본 인사 프로그램과 거의 똑같다.

파이참에서 대화식 셸을 실행하고, def1.py라는 새 파이썬 파일을 만든다. 새로 만든 파일에 다음 코드를 따라 입력한다. 코드의 의미는 아래에서 설명하니, 여기서는 정확하게 입력하는 것만 신경쓰면 된다. 주석은 따라 입력하지 않아도 된다.

코드 3-1 order() 함수 정의하기 (def1.py)

def order():                               # 끝에 콜론(:)을 빠트리지 않도록 주의
    print('주문하실 음료를 알려주세요')    # 이 블록은
    drink = input()                        # 앞에서부터 네 칸씩
    print(drink, '주문하셨습니다.')        # 들여쓰기한다

order()                                    # 여기에는 들여쓰기 하지 않는다

코드를 다 입력했으면 프로그램을 실행해 보자.

실행 결과:

주문하실 음료를 알려주세요
카페라테
카페라테 주문하셨습니다.

위와 같이 실행되면 성공이다. 혹시 오류가 발생한다면 다음 내용을 참고해 수정한다.

함수 정의에서 오류가 발생하는 경우

함수 정의하는 게 익숙하지 않을 때는 여러 가지 실수를 할 수 있다. 실습 중 오류가 발생했다면 책의 코드와 입력한 코드를 자세히 비교해보고 틀린 부분을 찾자.

  • 헤더 행은 콜론(:)으로 끝나야 한다. 콜론을 빠트리지 않도록 주의하자.
  • 함수의 본문은 정확히 네 칸씩 들여쓰기하여 작성해야 한다. 다음 스크린샷을 참고하자.

그림 3-2 함수 본문에서 들여쓰기를 하지 않았을 때

그림 3-2 함수 본문에서 들여쓰기를 하지 않았을 때

그림 3-3 함수 본문의 들여쓰기 깊이가 제각각일 때

그림 3-3 함수 본문의 들여쓰기 깊이가 제각각일 때

그림 3-4 함수 본문의 들여쓰기가 제각각일 때

그림 3-4 함수 본문의 들여쓰기가 제각각일 때

함수 정의 자세히 보기

그러면 우리가 처음으로 정의해 본 함수, order()를 자세히 살펴보자.

코드 3-2 order() 함수

def order():                             # ❶ 헤더 행
    print('주문하실 음료를 알려주세요')  # ❷ 본문 코드 블록
    drink = input()
    print(drink, '주문하셨습니다.')

order()                                  # ❸ 함수를 호출해야 실행된다

❶은 def 문의 헤더 행이다. 함수 정의는 헤더 행에서부터 시작된다. 함수의 이름을 order라고 정의했다.

❷는 함수의 본문 코드 블록이다. 이 코드는 함수가 호출되었을 때 실행된다. 몇 행이든 필요한 만큼 작성할 수 있다. 하지만 하나의 함수에서 너무 많은 일을 하지 않도록 주의하자. 함수의 내용이 너무 길면 내용을 파악하기 어려워진다.

❸은 함수를 호출하는 코드로, 함수 정의와는 별개다. def 문으로 함수를 정의하는 것만으로는 함수의 내용이 실행되지 않는다. 함수를 실행시키려면 호출해야 한다.

def 문이 낯설겠지만 어려운 것은 아니다. 이 장을 학습하는 동안 충분히 익숙해질 테니 걱정 마라. 들여쓰기를 잘못 하지 않도록 주의를 기울이면 된다.

연습문제

연습문제 3-3 함수 정의 연습

사용자로부터 정수를 입력받아 그 수의 절대값을 화면에 출력하는 함수 print_absolute()를 정의하고 호출해 보아라. 실행 결과는 다음과 같다.

정수를 입력하세요
-15
-15 의 절대값: 15

힌트: 사용자로부터 정수를 입력받을 때는 int(input()) 함수를 사용한다. (2장을 참고)

3.3.2 문서 문자열: 함수가 하는 일 설명하기

앞에서 만든 order() 함수를 다른 누군가가 사용한다고 생각해보자. 함수가 하는 일과 함수의 사용법을 이해하려면 코드를 직접 읽어보고 분석해야 할 것이다. 함수를 정의할 때 간단한 설명(도움말)을 남겨 두면, 다른 사람이 함수를 더 쉽게 이해하고 사용할 수 있다. 파이썬에서는 함수를 정의할 때 함수에 관한 설명을 작성해 둘 수 있다. 이 설명을 문서 문자열(docstring)이라고 한다. 문서 문자열은 def 문에서 헤더 행(함수 이름이 있는 첫 번째 행) 바로 다음 행에 큰따옴표 세 개(“”“)로 감싸 적는다.

def 함수이름():
    """문서 문자열"""
    함수의 내용

다음은 앞에서 만든 order() 함수에 문서 문자열을 추가한 것이다. 설명이 길어질 경우 문서 문자열을 여러 행에 나누어 입력해도 된다.

코드 3-3 문서 문자열을 추가한 order() 함수

def order():
    """사용자로부터 주문할 음료를 입력받아,
    주문 사항을 화면에 출력한다."""
    print('주문하실 음료를 알려주세요')
    drink = input()
    print(drink, '주문하셨습니다.')

1장에서 함수의 사용법을 출력하는 help() 함수를 소개했다. 함수의 문서 문자열을 정의해 둔 내용이 help() 함수로 함수의 도움말을 볼 때 나오는 내용이다. 문서 문자열은 필수는 아니지만 가급적 함수를 정의할 때 넣어주는 것이 좋다. 사람이 이해하기 쉬운 프로그램이 좋은 프로그램이다. 이 책의 함수에도 대부분 문서 문자열이 붙어 있다.

연습문제

연습문제 3-4 문서 문자열 입력하기

연습문제 3-3에서 만든 함수 print_absolute()에 적절한 문서 문자열을 입력하라. 그런 후에 함수를 실행해 보고 실행 결과에 영향이 있는지 확인해 보아라.

3.3.3 매개변수: 데이터를 전달받기

손님이 주문한 음료의 가격을 계산하는 함수를 만든다고 생각해보자. 주문받은 음료의 내역이 함수 속으로 전달되어야 계산이 가능할 것이다. 함수에 데이터를 전달하려면 어떻게 해야 할까?

print() 함수를 호출할 때, print('데이터')와 같이 호출한 것을 기억할 것이다. 이 때 괄호 속에 입력한 '데이터'가 함수에 전달하는 데이터다. 이처럼, 함수를 호출할 때 괄호 속에 데이터를 입력하여 함수 속으로 데이터를 전달할 수 있다.

매개변수 정의하기

전달된 데이터를 함수 속에서 사용하려면, 그 데이터를 함수 속에서 부를 이름(변수)을 정해 둬야 한다. 함수에 전달된 데이터를 대입하기 위한 변수를 매개변수(parameter)라고 부른다. 함수에 전달하는 데이터 자체는 인자(argument)라고 부른다. 즉, 함수를 호출하면 함수에 전달한 인자(데이터)가 함수 속의 매개변수에 대입된다.

매개변수는 def 문의 헤더 행에서 괄호 속에 정의한다.

def 함수이름(매개변수):      # 괄호 속에 매개변수 정의
    """문서 문자열"""
    함수의 내용

이 양식에 따라 매개변수를 갖는 함수를 정의해 보자.

코드 3-4 매개변수를 갖는 함수의 정의

def print_price(num_drink):                      # ❶ 매개변수(num_drink) 정의
    """음료의 잔 수(num_drink)을 전달받아,
    가격을 화면에 출력한다."""
    price_per_drink = 2500                       # 한 잔 당 가격
    total_price = num_drink * price_per_drink    # ❷ num_drink에 전달된 값 사용
    print('음료', drink, '잔:', total_price)     # ❸

print_price(3)                                   # ❹ 인자(3)를 전달하여 호출

실행 결과:

음료 3 잔: 7500

코드 3-4의 ❶을 보자. def 문의 헤더 행에서 함수 이름(print_price) 뒤의 괄호 속에 매개변수의 이름(num_drink)을 정의했다. 이렇게 정의해 둔 매개변수는 ❷, ❸과 같이 함수 본문에서 부를 수 있다. 매개변수는 함수를 호출할 때 전달하는 값으로 바뀐다. 함수를 호출할 때, ❹와 같이 매개변수에 대입할 값을 지정할 수 있다.

매개변수 여러 개 정의하기

함수는 여러 개의 인자를 전달받을 수도 있다. 다음 예와 같이, def 문에서 매개변수를 콤마(,)로 구분하여 나열하면 된다.

코드 3-5 여러 개의 인자를 전달받는 함수

def print_order(drink, cake):            # ❶ 콤마(,)로 구분해 매개변수 여러 개 정의
    """음료(drink)와 케익을(cake)를 전달받아,
    주문 내용을 화면에 출력한다."""
    print('음료:', drink, '/', '케익:', cake)

print_order('카페라테', '치즈케익')      # ❷ 함수에 여러 개의 인자를 전달하여 호출하기
print_order('당근케익', '우유')          # ❸ 전달하는 인자의 순서에 주의하자!

실행 결과:

음료: 카페라테 / 케익: 치즈케익
음료: 당근케익 / 케익: 우유

코드 3-5에서, ❶ def 문의 헤더 행에 두 매개변수 drink, cake를 정의했다. 이처럼 여러 매개변수를 콤마(,)로 구분하여 함수가 매개변수를 여러 개 갖도록 정의할 수 있다.

함수를 호출할 때는 ❷, ❸과 같이 매개변수의 개수만큼 인자를 입력하면 된다. 인자를 여러 개 입력할 때는 인자의 순서에 주의해야 한다. ❷를 실행하면 drinkcake에 각각 ‘카페라테’와 ‘치즈케익’이 올바르게 대입되지만, ❸과 같이 인자의 순서를 거꾸로 입력하면 음료와 케익이 뒤바뀌어 버린다!

함수에 인자를 전달할 때 개수나 순서를 좀 더 유연하게 조정하고 싶다면 어떻게 해야 할까? 그 방법은 3.5 절에서 알아볼 것이다. 지금은 인자와 매개변수를 일대 일로 순서대로 맞추는 것이 기본이라고 알아두자.

개념 정리

  • 인자: 함수를 호출할 때 함수에 전달하는 데이터
  • 매개변수: 함수에 전달된 데이터가 대입되는 변수
  • 함수를 호출할 때, 인자·매개변수의 개수·순서가 서로 같아야 한다.

연습문제

연습문제 3-5 매개변수가 있는 함수 정의하기

두 수를 전달받아 그 합계를 화면에 출력하는 함수 print_plus()를 정의하라. 그리고 이 함수에 100과 50을 전달하여 호출해 보아라.

힌트: 문서 문자열을 통해 함수가 하는 일을 설명하는 것도 잊지 말자.

3.3.4 return 문: 데이터를 반환하기

값을 반환하는 함수

함수 중에는 실행한 결괏값을 자신을 호출한 지점으로 되돌려주는 것들이 있다. 예를 들어, input() 함수는 사용자로부터 텍스트를 입력받아 실행 결과로 되돌려준다. 또, abs() 함수는 인자로 전달된 수의 절대값을 구해 실행 결과로 되돌려준다. 이렇게 함수가 만들어낸 데이터를 함수를 호출한 지점으로 되돌려주는 것을 ‘함수가 값을 반환한다’라고 말한다. 이처럼 함수가 실행 결과를 반환하도록 만들어 두면 복잡한 계산 과정을 함수 호출 한 번으로 대신할 수 있어 편리하다.

두 가지 출력을 구별하자

함수는 데이터를 화면에 출력하기도 하고, 함수 바깥으로 출력하기도 한다. 둘 다 ‘출력(output)’이라고 불리곤 하지만, 정확하게 표현하면 전자는 ‘프린트(print)’이고, 후자는 ‘반환(return)’이다. 프린트는 데이터를 사람에게 보여주기 위한 것이다. 반환은 프로그램의 진행을 위해 계산 결과를 함수를 호출한 지점으로 전달하는 것이다.

대화식 셸에서 함수를 실행하면 함수의 출력(반환)이 자동으로 화면에 출력(프린트)된다. 대화식 셸이 여러분을 위해 함수의 반환값을 화면에 프린트해주기 때문이다. 실제 프로그램에서는 print() 함수로 화면에 출력하도록 명령한 것만 화면에 프린트된다.

return 문

함수가 값을 반환하도록 하려면 return 문을 사용하면 된다. return 문을 실행하면 함수의 실행이 종료되고, 지정한 값이 함수가 호출된 지점으로 반환된다. return 문을 사용하는 양식은 간단하다. 함수 안에서 return 예약어 뒤에 반환할 데이터를 적어주기만 하면 된다.

def 함수이름:
    함수의 내용
    return 반환값

return 문을 사용하는 실제 예를 한 번 보자.

코드 3-6 return 문의 사용

def price(num_drink):
    """음료의 잔 수(num_drink)을 전달받아,
    가격을 반환한다."""
    price_per_drink = 2500
    total_price = num_drink * price_per_drink
    return total_price                         # ❶ 계산한 값 반환하기

result = price(3)                              # ❷ 함수 호출하고 반환값 저장하기
print('가격:', result)

실행 결과:

가격: 7500

위 코드의 price() 함수는 내부에서 계산한 값을 ❶의 return 문으로 반환한다. ❷와 같이 함수를 호출하여 함수의 실행 결과를 반환받을 수 있다.

함수의 실행을 중간에 끝내기

함수를 실행하면 함수에 정의된 내용이 위에서부터 아래로 차례대로 끝까지 실행된다. 그런데 함수의 실행을 중간에 끝내야 할 때도 있다. 그럴 때 return 문을 이용하면 된다. return 문은 함수의 값을 반환할 때 뿐 아니라 함수의 실행을 종료시킬 때도 쓸 수 있다.

코드 3-7 return 문으로 함수의 실행을 중간에 끝내기

def print_to_3():
    """1부터 3까지 화면에 출력한다"""
    print(1)
    print(2)
    print(3)
    return    # ❶ 여기서 함수의 실행이 끝난다
    print(4)  # ❷ 이 행부터는 실행되지 않는다
    print(5)

print_to_3()

실행 결과:

1
2
3

print_to_3() 함수를 실행하면 ❶의return 문 이전의 코드까지만 실행되고 ❷ 이후의 코드는 실행되지 않는 것을 확인할 수 있다. 사실 위의 예제는 return 문이 필요하지 않다. 그냥 print(4)print(5)를 지우면 되니 말이다. 함수의 실행을 중간에 끝내는 것은 6장에서 배우는 프로그램의 흐름을 제어하는 방법과 결합하면 좀 더 유용하게 활용할 수 있다. 지금은 return 문이 실행되면 함수의 실행이 끝난다는 것만 기억하자.

반환값이 없는 함수

함수를 정의할 때 return 문을 작성하지 않는 경우도 있다. return 문을 가지지 않은 함수는 끝까지 실행된 뒤에 None을 반환한다. 마찬가지로, 코드 3-7처럼 return 문이 있더라도 return 문에 반환값이 지정되지 않은 경우에도 함수가 None을 반환한다. None은 값이 없음을 뜻하는 특별한 값이다. 어떤 함수가 항상 None을 반환한다면, 반환값이 없는 함수라고 할 수 있다.

개념 정리

  • return 문을 이용하여 함수에서 값을 출력(반환)할 수 있다.
  • return 문이 실행되면 함수의 실행이 종료된다.
  • None은 값이 없음을 나타내는 값이다. 함수의 반환값을 지정하지 않으면 None이 반환된다.

연습문제

연습문제 3-6 매개변수와 반환값이 있는 함수 정의하기

네 개의 수를 매개변수 입력받아 평균을 계산해 반환하는 함수 average_of_4_numbers()를 정의하라. 이 함수를 이용해 512, 64, 256, 192의 평균을 계산해 화면에 출력하라.

연습문제 3-7 반환값이 없는 함수

아래와 같이 값을 반환하지 않는 함수를 하나 정의하고, 함수를 실행하여 반환된 값을 변수에 저장해라. 그 변수를 print() 함수로 화면에 출력해서 어떤 값이 저장되어 있는지 확인해 보아라.

def no_return():
    """화면에 메시지를 출력하지만, 값을 반환하지는 않는다."""
    print('이 함수에는 반환값이 없습니다.')

result = no_return()

연습문제 3-8 수식을 함수로 정의하기

함수를 이용하면 수학 공식을 외우지 않고, 컴퓨터에 기록해 둘 수 있다. 삼각형의 넓이를 구하는 공식 ‘밑볕 * 높이 / 2’를 함수로 정의해 보아라. 그 후 이 함수를 이용해 밑변의 길이가 10이고 높이가 8인 삼각형의 넓이를 계산해 출력하라.

힌트: 함수와 매개변수에 적절한 이름을 붙이자.

3.3.5 대화식 셸에서 함수 정의하기

대화식 셸에서도 함수를 정의할 수 있다. 프로그램 파일을 직접 작성할 때와 방법은 같다. 다만 파일을 편집할 때에 비해서 수정이 불편하니, 오타가 나지 않도록 좀 더 신경써야 한다. 연습삼아 대화식 셸에서 다음 코드를 따라 입력해 보자. def 문을 작성하고 마지막 행에서 엔터를 한 번 더 입력하면 함수 정의가 완료된다.

코드 3-8 대화식 셸에서 함수 정의하기

>>> def minus_8(x):
...     """정수 x를 입력받아 8을 뺀 값을 반환한다."""
...     return x - 8
... 

함수를 호출해서 함수 정의가 잘 됐는지 확인하자.

코드 3-9 대화식 셸에서 함수 호출하기

>>> minus_8(8)
0

>>> minus_8(5)
-3

>>> minus_8(minus_8(20) + minus_8(10))
6

책에 나오는 간단한 함수를 직접 입력해 볼 때는 대화식 셸을 사용하면 간편할 것이다.

대화식 셸에서 함수의 문서 문자열 확인하기

1장에서 소개했듯이, 대화식 셸에서는 함수의 이름을 help() 함수에 전달하여 도움말을 확인해볼 수도 있다. 문서 문자열로 정의해 둔 내용이 도움말로 출력된다.

코드 3-10 대화식 셸에서 함수 도움말 확인하기

>>> help(minus_8)
Help on function minus_8 in module __main__:

minus_8(x)
    정수 x를 입력받아 8을 뺀 값을 반환한다.

함수의 사용법이 잘 기억나지 않을 때는 대화식 셸을 열고 help() 함수를 이용하자.