파이썬 기초 강좌 16 함수 (Function)

14 분 소요

안녕하세요. 스쿨오브웹의 이상희입니다. 이번 강좌에서는 함수에 대해서 공부하도록 하겠습니다. 이 강좌에서는 편의를 위해서 주피터 노트북을 사용하겠습니다. 노트북 사용 방법을 잘 모르시는 분들은 아래 링크 따라서 이전 강좌를 참고하여 주시기 바랍니다.

주피터 노트북 사용법

프로그래밍에서의 함수란 특정 코드 블록을 재사용하기 위해 이름을 붙여서 네임스페이스에 저장한 오브젝트를 말합니다. 조금 더 풀어서 설명하자면 함수란 자주 사용되는 코드를 매번 다시 사용할 때마다 코딩하지 않도록 이름을 붙여 메모리에 저장해 놓고, 다시 사용하고 싶을 때는 정의한 이름으로 호출하여 저장해 놓은 코드 블록이 실행되게 하는 프로그램 스킴입니다. 혹시 프로그램의 기본 개념인 D.R.Y. 란 약어를 들어보신 적이 있으신가요? D.R.Y. 란 "Don’t Repeat Yourself"란 문장의 약어입니다. 즉 같은 행동을 반복하지 말라는 뜻이죠. 함수를 사용하면 같은 코드의 반복이 줄어 코드가 간결해지고, 코딩 시간이 줄어들 뿐만 아니라, 코드의 가독성이 높아져 디버깅을 더 쉽게 할 수 있게 됩니다.

D.R.Y. 는 “Don't Repeat Yourself" 라는 문장의 약어이며, 프로그래머라면 꼭 기억해두어야 하는 중요한 개념입니다.

먼저 함수를 정의하는 방법을 알아보겠습니다.

주피터 노트북
# def 키워드를 사용하여 함수를 정의합니다.
def hello():  # 함수의 이름은 hello 입니다.
    # 여기서 부터 return 키워드 전까지가 "function body" 입니다.
    """
    이 부분은 docstring 이고, 함수에 대한 설명이 들어갑니다.
    """
    print('Hello!')  # 함수가 호출되면 Hello! 라는 문자열을 출력합니다.
    return None  # return 키워드를 사용하여 결과 값을 리턴합니다.

위의 코드와 같이 함수를 정의하기 위해서는 def 키워드를 사용하셔야 합니다. def 키워드 뒤에는 함수의 이름을 정의합니다. 정의한 함수의 이름 뒤에는 괄호와 콜론을 입력합니다. 그 아래 줄 부터 마지막 줄까지가 "function body"라고 하는 부분이고, 이부분에 함수가 호출되면 실행될 코드 블록이 들어가게 됩니다. function body 가장 상단에는 함수에 대한 설명을 넣을 수가 있습니다. 이 부분을 "docstring"이라고 부르고 다음과 같이 __doc__ 메소드를 호출하거나, help 함수를 사용하였을 때 출력되는 부분입니다.

주피터 노트북
# def 키워드를 사용하여 함수를 정의합니다.
def hello():  # 함수의 이름은 hello 입니다.
    # 여기서부터 return 키워드 전까지가 "function body"입니다.
    """
    이 부분은 docstring 이고, 함수에 대한 설명이 들어갑니다.
    """
    print('Hello!')  # 함수가 호출되면 "Hello!" 라는 문자열을 출력합니다.
    return None  # return 키워드를 사용하여 함수의 결과 값을 리턴합니다.
 
 
print('[__doc__ 메소드를 사용할 경우]')
print(hello.__doc__)
 
print('[help 함수를 사용할 경우]')
help(hello)
결과
[__doc__ 메소드를 사용할 경우]
 
 부분은 docstring 이고, 함수에 대한 설명이 들어갑니다.
    
[help 함수를 사용할 경우]
Help on function hello in module __main__:
 
hello()
    이 부분은 docstring 이고, 함수에 대한 설명이 들어갑니다.

미리 정의한 함수는 아래 코드와 같이 함수이름에 괄호를 붙여 호출을 할 수 있습니다.

주피터 노트북
def hello():
    print('Hello!')
 
hello()
결과
Hello!

함수를 실행하기 위해서는 함수 이름 뒤에 괄호를 꼭 붙여야 한다는 점 잊지 마시기 바랍니다. 만약 괄호가 없이 함수의 이름만 입력한다면 함수 오브젝트만 리턴되고 함수는 실행되지 않습니다.

사용자가 정의한 함수를 사용자 정의 함수 (user defined function) 라고 하고, 조금 전에 실행한 help 함수와 같이 파이썬을 설치할 때 포함되어 사용자가 정의하지 않고도 사용할 수 있는 함수를 내장 함수 (built-in function) 라고 합니다. 파이썬은 편리한 built-in 함수를 많이 포함하고 있고, 이런 이유로 파이썬에는 베터리가 포함된 언어라고 합니다.

함수는 인자를 가질 수가 있습니다. 영어로는 parameter 라고 하고 매개변수라고도 불리는 인자는 함수를 정의할 때 괄호안에 정의 되고 함수안에서만 참조가 가능합니다.

주피터 노트북
def hello(name):  # 괄호 안에 정의되는 변수를 인자라고 합니다.
    print('Hello, {}!'.format(name))
 
# 함수를 호출할 때 괄호 안에 들어가는 것이 인수이고, 영어로는 argument라고 합니다.
hello('pink')
결과
Hello pink

함수 밖에서 name 매개변수를 참조하면 아래와 같이 name 오브젝트를 찾을 수 없다는 NameError가 발생합니다.

주피터 노트북
def hello(name):
    print('Hello, {}!'.format(name))
 
print(name)
결과
NameError: name 'name' is not defined

만약에 함수 밖에서 name 이라는 변수를 정의하고 함수를 실행하게 되면 어떻게 될까요?

주피터 노트북
def hello(name):
    print('Hello, {}!'.format(name))
 
name = 'sanghee'
hello('pink')
결과
Hello, pink!

그대로 핑크라는 이름이 출력되는 것을 볼 수 있습니다. 그렇다면 함수 밖의 정의한 name 변수의 값이 바꼈을까요? 확인해보도록 하겠습니다.

주피터 노트북
def hello(name):
    print('Hello, {}!'.format(name))
 
name = 'sanghee'
hello('pink')
print(name)
결과
Hello, pink!
sanghee

함수밖에 정의된 변수의 값이 변하지 않을 것을 볼 수 있습니다. 함수안에 정의되는 매개변수는 함수를 실행하면 함수의 네임스페이스에 잠깐 저장이 되었다가 없어지는 변수이며, 같은 이름을 가지고 있다고 하더라도 함수밖에 정의된 변수와는 전혀 다른 공간에 존재하는 전혀 다른 오브젝트라는 것을 꼭 기억하시기 바랍니다.

반대로 함수밖의 변수와 같은 이름을 가진 새로운 변수를 함수안에 정의하여도 함수밖의 변수에 영향을 미치지 않습니다.

주피터 노트북
name = 'sanghee'
 
def hello(name):
    name = 'you are {}'.format(name)
    print('Hello, {}!'.format(name))
 
hello('pink')
 
print(name)
결과
Hello, you are pink!
sanghee

왜 이렇게 되는지를 이해하시려면 네임스페이스라는 개념과 원리를 이해하셔야 하는데, 네임스페이스에 대한 설명은 기초 강좌를 마치고 별도의 강좌에서 자세히 설명해드리도록 하겠습니다.

인자를 가진 함수를 호출할 때는 꼭 인수와 함께 호출을 해야 합니다. 인수가 없이 실행한다면 아래와 같이 TypeError가 발생합니다.

주피터 노트북
def hello(name):
    print('Hello, {}!'.format(name))
 
hello()
결과
TypeError: hello() missing 1 required positional argument: 'name'

이런 에러를 방지하기 위해서 매개변수에 디폴트 값을 전달할 수도 있습니다. 이 경우에 어떤 인수도 전달되지 않으면 디폴트 값이 사용됩니다.

주피터 노트북
def hello(name='pink'):
    print('Hello, {}!'.format(name))
 
hello()
결과
Hello, pink!

하지만 디폴트 값이 있어도 인수를 전달하면, 전달된 인수가 디폴트 값을 오버라이트하게 됩니다.

주피터 노트북
def hello(name='pink'):
    print('Hello, {}!'.format(name))
 
hello('sanghee')
결과
Hello, sanghee!

함수의 인자는 한 개뿐만 아니라 여러 개를 정의하여 여러 개의 인수를 받을 수도 있습니다. 두 개의 숫자를 입력받아 합을 구하는 함수를 만들어보겠습니다.

주피터 노트북
def add(x, y):
    print(x + y)
 
add(1, 1)
결과
2

사실 함수를 사용하는 대부분의 목적은 결과값을 출력하기보다는 함수 안에서 처리된 결과값을 또 다른 변수에 저장하여 재활용하기 위해서 결과값을 리턴하는 경우가 더 많습니다. 함수에서 처리된 결과값을 리턴하기 위해서는 return 키워드를 사용해야 합니다.

주피터 노트북
def add(x, y):
    return x + y
 
result = add(1, 1)
print(result)
결과
2

결과값을 리턴함으로써 함수의 결과값을 다른 함수의 인수로 사용할 수도 있습니다.

주피터 노트북
def add(x, y):
    return x + y
 
result = add(add(1, 1), add(1, 1))
print(result)
결과
4

함수는 하나의 값만 리턴할 수 있는 게 아니라 여러 개의 값을 리턴할 수도 있습니다. 한 단어를 인자로 받아 첫 번째 글자와 마지막 글자를 리턴하는 함수를 만들어보겠습니다.

주피터 노트북
def first_and_last(word):
    return word[0], word[-1]
 
print(first_and_last('letter'))
결과
('l', 'r')

함수가 두 개의 값을 리턴하면 튜플이 리턴되는 것을 볼 수 있습니다. 이런 경우, 언패킹 기능을 사용하여 리턴된 값을 두 개의 변수에 저장할 수 있습니다.

주피터 노트북
def first_and_last(word):
    return word[0], word[-1]
 
first, last = first_and_last('letter')
print('첫글자: {}, 마지막 글자: {}'.format(first, last))
결과
첫글자: l, 마지막 글자: r

다음과 같은 리스트가 있고 리스트의 모든 아이템의 합을 구해하는 프로그램을 만든다고 해보죠.

주피터 노트북
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]

물론 아래 코드와 같이 built-in 함수인 sum 함수를 사용하면 아주 간단히 해결의 됩니다.

주피터 노트북
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]
 
print(sum(list_1))
print(sum(list_2))
print(sum(list_3))
결과
6
15
24

하지만 빌트인 함수가 없다고 가정하고 프로그램을 만든다면, 다음과 같은 코드가 만들어 질 수 있습니다.

주피터 노트북
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]
 
total = 0
for i in list_1:
    total += i
print(total)
 
total = 0
for i in list_2:
    total += i
print(total)
 
total = 0
for i in list_3:
    total += i
print(total)
결과
6
15
24

이 코드를 보면 우리가 원하는 결과 값은 얻었지만 같은 패턴의 코드가 계속 반복되는 것을 볼 수 있습니다. 이렇게 같은 코드가 여러 번 반복된다면 프로그램의 기본 개념인 D.R.Y. 개념에 반하는 좋지 않은 코드입니다. 이런 경우에는 아래의 코드와 같이 반복되는 부분을 함수로 정의하여 재활용하여야 합니다.

주피터 노트북
list_1 = [1, 2, 3]
list_2 = [4, 5, 6]
list_3 = [7, 8, 9]
 
def get_total(a_list):
    total = 0
    for i in a_list:
        total += i
    return total
 
print(get_total(list_1))
print(get_total(list_2))
print(get_total(list_3))
결과
6
15
24

어떤가요? 코드가 더 간결해진 것을 알 수 있고, 나중에 같은 연산을 또 해야 할 때는 get_total이라는 함수를 호출하기만 하면 되는 것입니다. 이런 식으로 코드의 반복을 줄이고 더 좋은 코드를 만들기 위해 코드를 수정하는 작업을 "refactoring"이라고 하며, 전문 개발자들도 수 없이 많은 refactoring 작업을 통해 더 좋은 프로그램을 만들기 위해 끊임없이 노력합니다. 여러분들도 코딩이 끝난 프로그램을 다시 리뷰하고, 함수를 사용하여 반복되는 부분을 없애는 좋은 습관을 기르도록 하십시오.

오늘의 강좌는 여기에서 마치도록 하고, 다음 강좌에서는 파일의 읽기, 쓰기에 대해서 공부하도록 하겠습니다.