본문 바로가기
코드/python

파이썬 함수 관련

by WeZZ 2012. 1. 3.

1) 함수

함수를 설명하기 전에 믹서기를 생각해보자. 우리는 믹서기에 과일을 넣는다. 그리고 믹서를 이용해서 과일을 갈아서 과일 쥬스를 만들어 낸다. 이러한 일들은 우리의 생활 주변에서 언제든지 찾아 볼 수 있는 일이다. 우리가 믹서기에 넣는 과일은 입력이 되고 과일 쥬스는 그 출력(리턴값)이 된다. 그렇다면 믹서기는 무엇인가?

(by http://www.wpclipart.com)

바로 우리가 여기서 알고자 하는 함수이다. 입력을 가지고 어떤 일을 수행한 다음에 결과물을 내어놓는 것, 이것이 함수가 하는 일이다. 우리는 어려서부터 함수가 무엇인지에 대해서 공부했지만 이것에 대해서 깊숙이 고민해 본 적은 별로 없다. 하지만 우리는 함수에 대해서 조금 더 생각해 보는 시간을 가져야 한다. 프로그래밍에 있어서 함수란 것은 정말 중요하기 때문이다.

일차함수 y = 2x + 3 이것도 함수이다. 하지만 우리는 이것을 수학시간에 배웠던 직선그래프로만 알고 있지 x에 어떤 값을 넣었을 때 어떤 변화에 의해서 y값이 나온다는 생각은 대부분 해보지 않았을 것이다.

생각을 달리하고 파이썬 함수에 대해서 깊이 들어가 보도록 하자.


함수를 사용하는 이유?

가끔 프로그래밍을 하다 보면 똑같은 내용을 자신이 반복해서 적고 있는 것을 발견할 때가 있다. 이 때가 바로 함수가 필요한 때이다. 여러 번 반복해서 사용된다는 것은 언제고 또다시 사용할 만한 가치가 있는 부분이라는 뜻이다. 즉, 이러한 경우 이것을 한 뭉치로 묶어서 “어떤 입력값을 주었을 때 어떤 리턴값을 돌려준다”라는 식의 함수를 작성하는 것이 현명한 일일 것이다.

함수를 사용하는 또 다른 이유로는 자신이 만든 프로그램을 함수화 시켜 놓으면 프로그램의 흐름을 일목요연하게 감지할 수 있게 된다. 마치 큰 공장에서 한 물건이 나올 때 여러 가지 공정을 거쳐가는 것처럼 자신이 원하는 결과값이 함수들을 거쳐가면서 변화되는 모습을 볼 수 있다.

이렇게 되면 프로그램의 흐름도 잘 파악할 수 있게 되고 에러가 어디에서 나는지도 금방 알아차릴 수 있게 된다. 함수를 잘 이용하고 적절한 함수를 만들 줄 아는 사람을 훌륭한 프로그래머라고 할 것이다.


파이썬 함수의 구조

파이썬의 함수의 모습은 다음과 같다.

def 함수명(입력 인수):
    <수행할 문장1>
    <수행할 문장2>
    ...

def는 함수를 만들 때 사용된다. 함수명은 함수를 만드는 사람이 임의로 만드는 것이다. 마치 변수이름을 정하는 것과 같은 이치이다. 함수명 다음에 괄호 안에 있는 입력인수라는 것은 이 함수에 입력으로 넣어주는 값이다. 입력 인수의 개수에는 제한이 없다. 다음에 if, while, for문 등과 마찬가지로 수행할 문장을 수행한다.


가장 간단하지만 많은 것을 설명해 주는 다음의 예를 보도록 하자.

def sum(a, b): 
    return a + b

위 함수의 의미는 다음과 같이 정의된다.

“sum이라는 것은 함수명이고 입력값으로 두개의 값을 받으며 리턴값은 두 개의 입력값을 더한 값이다.”


여기서 return은 함수의 결과 값을 돌려주는 명령어이다. 직접 위의 함수를 만들어 보고 사용해 보자.

>>> def sum(a, b):
...     return a+b
...
>>>

위와 같이 sum함수를 먼저 만들자.

>>> a = 3 
>>> b = 4 
>>> c = sum(a, b) 
>>> print c 
7

a에 3, b에 4를 대입한 다음 이미 만들었던 sum함수에 a와 b를 입력값으로 주어서 c라는 함수의 리턴값을 돌려 받는다. print c로 c의 값을 확인할 수 있다.


함수의 입력값과 리턴값

프로그래밍을 공부할 때 어려운 부분 중 하나가 용어의 혼용이라고 할 수 있다. 많은 원서들을 보기도 하고 누군가의 번역본을 보기도 하면서 우리는 갖가지 용어들을 익힌다. 입력 값을 다른 말로 함수의 인수, 입력인수 등으로 말하기도 하고 리턴 값을 출력 값, 결과 값, 돌려주는 값 등으로 말하기도 한다. 이렇듯 많은 용어들이 다른 말로 표현되지만 의미는 동일한 경우가 많다. 이런 것들에 대해 기억해 놓아야만 머리가 덜 아플 것이다.


함수는 들어온 입력값을 가지고 어떤 처리를 하여 적절한 리턴값을 돌려주는 블랙박스와 같다.

입력값 ---> 함수(블랙박스) ----> 리턴값


함수에 들어오는 입력값과 리턴값에 대해서 자세히 알아보도록 하자.


평범한 함수

입력 값이 있고 리턴값이 있는 함수가 평범한 함수이다. 앞으로 독자가 프로그래밍을 할 때 만들 함수의 대부분은 이러한 형태일 것이다.

def 함수이름(입력인수):
    <수행할 문장>
    ...
    return 결과값


평범한 함수의 전형적인 예를 한번 보도록 하자.

def sum(a, b): 
    result = a + b 
    return result

두 개의 입력값을 받아서 서로 더한 결과값을 돌려주는 함수이다.


위의 함수를 사용하는 방법은 다음과 같다. 다음을 따라 해 보자. 우선 sum함수를 만들자.

>>> def sum(a, b): 
...     result = a + b 
...     return result 
... 
>>>


다음에 입력값을 주고 리턴값을 돌려 받아 보자.

>>> a = sum(3, 4) 
>>> print a 
7


위처럼 입력값과 리턴값이 있는 함수는 다음처럼 사용된다.

리턴값받을변수 = 함수명(입력인수1, 입력인수2, , ,)


입력값이 없는 함수

입력값이 없는 함수가 존재할까? 당연히 그렇다. 다음을 보자.

>>> def say(): 
...     return 'Hi' 
... 
>>>

say라는 이름의 함수를 만들었다. 하지만 함수이름 다음의 입력 인수부분을 나타내는 괄호 안이 비어있다.


이 함수를 어떻게 쓸 수 있을까? 다음과 같이 따라해 보자.

>>> a = say() 
>>> print a 
Hi

위의 함수를 쓰기 위해서는 say()처럼 괄호 안에 아무런 값도 넣어주지 않고 써야 한다. 위의 함수는 입력값은 없지만 리턴값으로 'Hi'라는 문자열을 돌려준다. 따라서 a = say()처럼 하면 a에는 'Hi'라는 문자열이 대입되게 되는 것이다.


즉, 입력값이 없고 리턴값만 있는 함수는 다음과 같이 사용된다.

리턴값받을변수 = 함수명()


리턴값이 없는 함수

리턴값이 없는 함수 역시 존재한다. 다음의 예를 보자.

>>> def sum(a, b): 
...     print "%d, %d의 합은 %d입니다." % (a, b, a+b) 
... 
>>>


리턴값이 없는 함수는 돌려주는 값이 없기 때문에 다음과 같이 사용한다.

>>> sum(3, 4) 
3, 4 합은 7입니다.


즉, 리턴값이 없는 함수는 다음과 같이 사용된다.

함수명(입력인수1, 입력인수2, , ,)


실제로 리턴값이 없는지 알아보기 위해 다음의 예를 따라해 보자.

>>> a = sum(3, 4) 
3, 4 합은 7입니다.

아마도 독자는 다음과 같은 질문을 할지도 모른다. "3, 4의 합은 7입니다.“라는 문장을 출력해 주었는데 리턴값이 없는 것인가? 이 부분이 초보자들이 혼동스러워 하는 부분이기도 한데 print 문은 함수내에서 사용되어지는 문장일 뿐이다. 돌려주는 값은 당연히 없다. 돌려주는 값은 return 명령어로만 가능하다.


이것을 확인해 보기 위해서 돌려받는 값을 a라는 변수에 대입하여 출력해 보면 리턴값이 있는지 알 수 있다.

>>> print a 
None

a의 값이 None이다. 이 None이란 것은 거짓을 나타내는 자료형이라고 언급한 적이 있었다. sum함수처럼 돌려주는 값이 없을 때 a = sum(3, 4)처럼 쓰게 되면 함수 sum은 돌려주는 값으로 a변수에 None을 돌려주게 된다. 그렇다고 이것이 돌려주는 값이 있다는 걸로 생각하면 곤란하다.


입력값도 리턴값도 없는 함수

이것 역시 존재한다. 다음의 예를 보자.

>>> def say(): 
...     print 'Hi' 
... 
>>>

입력 값을 받는 곳도 없고 return문도 없으니 입력값도 리턴값도 없는 함수이다.


이것을 사용하는 방법은 단 한가지이다.

>>> say() 
Hi


즉, 입력값도 리턴값도 없는 함수는 다음과 같이 사용된다.

함수명()


입력값이 몇 개가 될 지 모를 때는 어떻게 해야 할까?

입력값을 주었을 때 그 입력값들을 모두 더해주는 함수를 생각해 보자. 하지만 몇 개가 입력으로 들어올지 알 수 없을 때는 어떻게 해야 할지 난감할 것이다. 이에 파이썬에서는 다음과 같은 것을 제공한다.

def 함수이름(*입력변수): 
    <수행할 문장>
    ...

입력인수 부분이 “*입력변수”로 바뀌었다.


다음의 예를 통해 사용법을 알아보자. sum_many(1,2) 하면 3을 sum_many(1,2,3)이라고 하면 6을 sum_many(1,2,3,4,5,6,7,8,9,10)은 55를 돌려주는 함수를 만들어 보자.

>>> def sum_many(*args): 
...     sum = 0 
...     for i in args: 
...         sum = sum + i 
...     return sum 
... 
>>>

위에서 만든 sum_many라는 함수는 입력값이 몇 개든지 상관이 없다. 그 이유는 args라는 변수가 입력값들을 전부 모아서 터플로 만들어 주기 때문이다. 만약 sum_many(1, 2, 3)처럼 이 함수를 쓴다면 args는 (1, 2, 3)이 되고 sum_many(1,2,3,4,5,6,7,8,9,10)처럼 하면 args는 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)이 된다. 여기서 *args라는 것은 임의로 정한 변수명이다. *pey, *python처럼 아무이름으로 해도 된다. 단 args라는 것은 입력인수를 뜻하는 영어단어인 arguments라는 영어의 약자로 관례적인 표기법임을 알아두도록 하자.


실제로 한번 테스트를 해 보도록 하자.

>>> result = sum_many(1,2,3) 
>>> print result 
6 
>>> result = sum_many(1,2,3,4,5,6,7,8,9,10) 
>>> print result 
55


def sum_many(*args) 처럼 *args만이 입력 인수로 올 수 있는 것은 아니다. 다음의 예를 보도록 하자.

>>> def sum_mul(choice, *args): 
...     if choice == "sum": 
...         result = 0 
...         for i in args: 
...             result = result + i 
...     elif choice == "mul": 
...         result = 1 
...         for i in args: 
...             result = result * i 
...     return result 
... 
>>>

위의 예는 입력 인수로 choice와 *args라는 것을 받는다. 따라서 다음과 같이 쓸 수 있을 것이다. sum_mul('sum', 1,2,3,4) 또는 sum_mul('mul', 1,2,3,4,5)처럼 choice부분에는 'sum'이나 'mul'이라는 문자열을 그리고 그 뒤에는 개수에 상관없는 숫자를 입력으로 준다.


실제로 한번 테스트를 해 보도록 하자.

>>> result = sum_mul('sum', 1,2,3,4,5) 
>>> print result 
15 
>>> result = sum_mul('mul', 1,2,3,4,5) 
>>> print result 
120


함수의 리턴값은 언제나 하나이다.

먼저 다음의 함수를 만들어 보자.

>>> def sum_and_mul(a,b): 
...     return a+b, a*b


이것을 다음과 같이 호출하면 어떻게 될까?

>>> a = sum_and_mul(3,4)

리턴값은 a+b와 a*b 두 개인데 리턴값을 받아들이는 변수는 a 하나만 쓰였으니 에러가 나지 않을까? 당연한 의문이다. 하지만 에러는 나지 않는다. 그 이유는 리턴값이 두 개가 아니라 하나라는 데 있다. 함수의 리턴값은 터플값 하나로 돌려주게 된다.


즉 a 변수는 위의 sum_and_mul 함수에 의해서 다음과 같은 값을 가지게 된다.

a = (7, 12)


즉, 리턴값으로 (7, 12)라는 터플 값을 갖게 되는 것이다.

이것을 두 개의 리턴값처럼 받고 싶다면 다음과 같이 호출하면 될 것이다.

>>> sum, mul = sum_and_mul(3, 4)

이렇게 호출한다면 이것의 의미는 sum, mul = (7, 12)가 되어서 sum은 7이 되고 mul은 12가 될 것이다.


또, 다음과 같은 의문이 생길 수도 있다.

>>> def sum_and_mul(a,b): 
...     return a+b 
...     return a*b 
... 
>>>

위와 같이 하면 두 개의 리턴값을 돌려주지 않을까? 하지만 파이썬에서 위와 같은 함수는 참 어리석은 함수이다.


위의 함수는 다음과 완전히 동일하다.

>>> def sum_and_mul(a,b): 
...     return a+b 
... 
>>>

즉, 함수는 return문을 만나는 순간 return값을 돌려준 다음에 함수를 빠져나가게 된다.


return의 또 다른 쓰임새

특별한 경우에 함수를 빠져나가기를 원할 때 return만 단독으로 써서 함수를 즉시 빠져나갈 수 있다. 다음 예를 보자.

>>> def say_nick(nick): 
...     if nick == "바보": 
...         return 
...     print "나의 별명은 %s 입니다." % nick 
... 
>>>

위의 함수는 입력값으로 nick이란 변수를 받아서 문자열을 출력하는 함수이다. 이 함수 역시 리턴값은 없다. (문자열을 출력한다는 것과 리턴값이 있다는 것은 전혀 다른 말이다. 혼동하지 말도록 하자, 함수의 리턴값은 오로지 return문에 의해서만 생성된다.) 만약에 입력값으로 '바보'라는 값이 들어오면 문자열을 출력하지 않고 함수를 즉시 빠져나간다. 위의 함수는 매우 쓸모 없는 함수이지만 이 return문으로 함수를 빠져나가는 방법은 실제 프로그래밍에서 자주 쓰인다.


입력값에 초기치 설정하기

이젠 좀더 다른 함수의 인수 전달 방법에 대해서 알아보자. 인수에 초기치를 미리 설정해 주는 경우를 보자.

def say_myself(name, old, sex=1): 
    print "나의 이름은 %s 입니다." % name 
    print "나이는 %d살입니다." % old 
    if sex: 
        print "남자입니다." 
    else: 
        print "여자입니다."

(※ 알아두기 - 앞으로 나올 프로그램 소스는 '>>>'표시가 없으면 항상 에디터로 작성하기로 하자.)

위의 함수를 보면 입력 인수가 세 개임을 알수 있다. 그런데 이상한 것이 나왔다. sex=1처럼 입력 인수에 미리 값을 넣어준 것이다. 이것이 바로 함수의 인수 초기치를 설정하는 방법이다. 항상 변하는 것이 아니라 아주 가끔 변하는 것일 때 이렇게 함수의 초기치를 미리 설정해 주는 것은 매우 유용하다.


위의 함수는 다음처럼 사용할 수 있다.

say_myself("박응용", 27)
say_myself("박응용", 27, 1)

즉 인수값으로 “박응용”, 27처럼 두 개를 주게 되면 name에는 "박응용“이 old에는 27이 sex라는 변수에는 입력값을 주지 않았지만 초기값인 1이라는 값을 갖게 되는 것이다. 따라서 위에서 함수를 사용한 두가지 방법은 모두 동일한 결과를 출력하게 된다.

say_myself("박응선", 27, 0)

초기값설정된 부분을 0으로 바꾸었으므로 이번에는 sex변수에 0이라는 값이 대입되게 된다.


함수 입력값 초기치 설정시 주의사항

함수의 초기치를 설정할 때 주의해야 할 것이 하나 있다.


만약 위의 함수를 다음과 같이 만들면 어떻게 될까?

def say_myself(name, sex=1, old): 
    print "나의 이름은 %s 입니다." % name 
    print "나이는 %d살입니다." % old 
    if sex: 
        print "남자입니다." 
    else: 
        print "여자입니다."

위의 함수와 바뀐 부분은 초기치를 설정한 인수의 위치이다. 결론을 미리 말하자면 이것은 실행시에 에러가 난다.


그냥 얼핏 생각하기에 위의 함수를 호출하려면 다음과 같이 하면 될 것 같다.

say_myself(“박응용”, 27)

하지만 위와 같이 함수를 호출한다면 name변수에는 "박응용“이 들어가겠지만 파이썬 인터프리터는 sex에 27을 대입해야 할지 old에 27을 대입해야 할지 알 수 없을 것이다.


에러메시지를 보면 다음과 같다.

SyntaxError: non-default argument follows default argument

위의 에러메시지는 초기 치를 설정해 놓은 입력 인수 뒤에 초기 치를 설정해 놓지 않은 입력 인수는 사용할 수 없다는 말이다. 즉 입력인수로 (name, old, sex=1)은 되지만 (name, sex=1, old)는 안된다는 것이다. 결론은 초기화 시키고 싶은 입력 변수들은 항상 뒤쪽에 위치시키라는 것이다.


함수 내에서 선언된 변수의 효력 범위

함수안에서 사용할 변수의 이름을 함수 밖에서 사용한 이름과 동일하게 사용한다면 어떻게 될까? 이런 궁금증이 떠올랐던 독자라면 이곳에서 확실하게 알 수 있을 것이다.


아래의 예를 보자.

a = 1 
def vartest(a): 
    a = a +1 

vartest(a) 
print a

먼저 a라는 변수를 생성하고 1을 대입한다. 그리고 입력으로 들어온 값을 1만큼 더해주고 리턴값은 돌려주지 않는 vartest 함수에 입력 값으로 a를 주었다. 그 다음에 a의 값을 출력하게 하였다. 당연히 vartest에서 a를 1만큼 더했으니까 2가 출력되어야 할 것 같지만 프로그램 소스를 작성해서 실행시켜 보면 결과 값이 1이 나온다.

그 이유는 함수안에서 새로 만들어진 변수는 함수안에서만 쓰여지는 변수이기 때문이다. 즉 def vartest(a)처럼 하면 이때 입력 인수를 뜻하는 변수 a는 함수안에서만 쓰이는 변수이지 함수 밖의 변수 a가 아니라는 말이다.


위에서 변수이름을 a 로한 vartest함수는 다음처럼 변수이름을 b로한 vartest와 완전히 동일한 것이다.

def vartest(b): 
    b = b + 1

즉 함수에서 쓰이는 변수는 함수 밖의 변수이름들과는 전혀 상관 없다는 말이다. 따라서 위에서 보았던 vartest의 a는 1이란 값을 입력으로 받아서 1만큼 더해주어 a가 2가 되지만 그것은 함수 내의 a를 뜻하는 것이지 함수 밖의 변수 a와는 전혀 다르다는 말이다.


다음의 예를 보면 더욱 정확하게 이해할 수 있을 것이다.

def vartest(a): 
    a = a + 1 

vartest(3) 
print a

위의 프로그램 소스를 에디터로 작성해서 실행시키면 어떻게 될까? 에러가 날 것이라 생각한 독자는 모든 것을 이해한 독자이다. vartest(3)을 수행하면 vartest라는 함수내에서 a는 4가 되지만 함수를 끝내고 난뒤에 print a라는 문장은 에러가 나게 된다. 그 이유는 a라는 변수는 어디에도 찾아 볼 수가 없기 때문이다. 다시 말하지만 함수 내에서 쓰이는 변수는 함수내에서 쓰일 뿐이지 함수 밖에서 사용될 수 있는 것이 아니다. 이것에 대해서 이해하는 것은 매우 중요하다.


그렇다면 vartest라는 함수를 이용해서 함수 외부의 a를 1만큼 증가시킬 수 있는 방법은 없을까? 라는 의문이 떠오르게 된다. 두 가지 해결 방법이 있을 수 있다.


첫 번째 방법

a = 1 
def vartest(a): 
    a = a +1 
    return a 

a = vartest(a) 
print a

첫 번째 방법은 return을 이용하는 방법이다. vartest함수는 입력으로 들어온 값을 1만큼 더한 값을 돌려준다. 따라서 a = vartest(a)처럼 하면 a가 vartest함수의 리턴값으로 바뀌게 되는 것이다. 여기서도 물론 vartest함수 안의 a변수는 함수 밖의 a와는 다른 것이다.


두 번째 방법

a = 1 
def vartest(): 
    global a 
    a = a+1 

vartest() 
print a

두 번째 방법은 global이라는 명령어를 이용하는 방법이다. 위의 예에서 보듯이 vartest안의 global a라는 문장은 함수 안에서 함수 밖의 a변수를 직접 사용하겠다는 말이다. 보통 프로그래밍을 할 때 global이라는 것은 쓰지 않는 것이 좋다. 왜냐하면 함수는 독립적으로 존재하는 것이 좋기 때문이다. 외부 변수에 종속적인 함수는 그다지 좋은 함수가 아니다. 독자는 가급적 이 global을 쓰는 방식을 피해야 할 것이다. 따라서 당연히 두 번째 방법보다는 첫 번째 방법이 좋다.


출처 : http://codejob.co.kr/docs/page/42/