이번에는 함수와 프로그램의 입출력에 대해 알아본다.


1. 함수

프로그래밍을 하다 보면 똑같은 코드가 반복적으로 사용되어야 할 때가 많은데 이 때 필요한 것이 함수이다. 즉 반복되는 부분이 있을 경우 “반복적으로 사용되는 가치 있는 부분”을 한 뭉치로 묶어서 “어떤 입력값을 주었을 때 어떤 결괏값을 돌려준다”라는 식의 함수로 작성하는 것이 현명하다.

함수를 작성할 때는 함수 내부에서 사용되는 변수의 값을 전달받기 위해서 매개변수를 정의할 수 있다. 이후에 함수에서 어떤 값을 반환할 때는 return을 이용한다. 경우에 따라서 매개변수와 return 문은 존재하지 않을 수도 있다.

def 함수명(매개변수):
    <수행할 문장1>
    <수행할 문장2>
    ...
    return 반환 

일반적인 함수

입력값이 있고 결괏값이 있는 함수가 일반적인 함수이다.

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

a = add(3,4)
print(a)

# 7

입력값이 없는 함수

입력값이 없고 결괏값만 있는 함수는 ‘결괏값을 받을 변수 = 함수이름()’처럼 사용한다.

def say():
  return 'Hi' 

a = say()
print(a)
# Hi

위 함수를 쓰기 위해서는 say()처럼 괄호 안에 아무 값도 넣지 않아야 한다. 이 함수는 입력값은 없지만 결괏값으로 Hi라는 문자열을 돌려준다. a = say()처럼 작성하면 a에 Hi 문자열이 대입되는 것이다.

결괏값이 없는 함수

결과값이 없는 함수는 ‘함수이름(입력인수1, 입력인수2, …)’ 처럼 사용한다.


def add(a, b): 
  print(a+b)
add(3,4)
# 7

입력값도 결괏값도 없는 함수

입력 인수를 받는 매개변수도 없고 return문도 없으니 입력값도 결괏값도 없는 함수이다. 입력값도 결괏값도 없는 함수는 ‘함수이름()’ 처럼 사용한다.

def say(): 
  print('Hi')
  
say()
# Hi

매개변수 지정하여 호출하기

함수를 호출하는 과정에서 매개변수를 지정하면 다음과 같이 순서에 상관없이 사용할 수 있다는 장점이 있다.

def add(a, b):
  return a+b
  
result = add(b=3, a=7)  # a에 3, b에 7을 전달
print(result)
# 10

입력값이 몇 개가 될지 모를 때

일반적으로 볼 수 있는 함수 형태에서 괄호 안의 매개변수 부분이 *매개변수 로 바뀌었다.

def 함수이름(*매개변수): 
    <수행할 문장>
    ...
    return

이를 사용해 여러 개의 입력값을 모두 더하는 함수를 직접 만들어 보자. 예를 들어 add_many(1, 2)이면 3을, add_many(1,2,3)이면 6을, add_many(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)이면 55를 돌려주는 함수를 만들어 보자.

def add_many(*args): 
  result = 0 
  for i in args: 
    result = result + i 
  return result 
  
result = add_many(1,2,3)
print(result)
# 6

*args처럼 매개변수 이름 앞에 *을 붙이면 입력값을 전부 모아서 튜플로 만들어 준다.

def add_mul(choice, *args): 
  if choice == "add": 
    result = 0 
  for i in args: 
    result = result + i 
  elif choice == "mul": 
    result = 1 
  for i in args: 
    result = result * i 
  return result 
  
result = add_mul('add', 1,2,3,4,5)
print(result)
# 15

키워드 파라미터 kwargs

키워드 파라미터를 사용할 때는 매개변수 앞에 별 두 개(**)를 붙인다. **kwargs처럼 매개변수 이름 앞에 **을 붙이면 매개변수 kwargs는 딕셔너리가 되고 모든 key=value 형태의 결괏값이 그 딕셔너리에 저장된다.

def print_kwargs(**kwargs):
  print(kwargs)
  
print_kwargs(a=1)
# {'a': 1}

print_kwargs(name='foo', age=3)
# {'age': 3, 'name': 'foo'}

함수의 결괏값은 언제나 하나이다

함수의 결괏값은 언제나 1개이다.

def add_and_mul(a,b): 
  return a+b, a*b
  
result = add_and_mul(3,4)
result
# (7, 12)

add_and_mul 함수의 결괏값 a+b와 ab는 튜플값 하나인 (a+b, ab)로 돌려준다. 만약 이 하나의 튜플 값을 2개의 결괏값처럼 받고 싶다면 다음과 같이 함수를 호출하면 된다.

result1, result2 = add_and_mul(3, 4)

함수를 즉시 빠져나가기

특별한 상황일 때 함수를 빠져나가고 싶다면 return을 단독으로 써서 함수를 즉시 빠져나갈 수 있다.

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

say_nick('야호')
# 나의 별명은 야호입니다.
say_nick('바보')
# 

만약에 입력값으로 ‘바보’라는 값이 들어오면 문자열을 출력하지 않고 함수를 즉시 빠져나간다.

매개변수에 초깃값 미리 설정하기

함수의 매개변수에 들어갈 값이 항상 변하는 것이 아닐 경우에는 매개변수에 초깃값을 미리 설정해 주면 유용하다. 이때 초기화시키고 싶은 매개변수를 항상 뒤쪽에 놓아야 한다.

def say_myself(name, old, man=True): 
    print("나의 이름은 %s 입니다." % name) 
    print("나이는 %d살입니다." % old) 
    if man: 
        print("남자입니다.")
    else: 
        print("여자입니다.")
        
say_myself("박응선", 27, False)
'''
나의 이름은 박응선입니다.
나이는 27살입니다.
여자입니다.
''''

함수 안에서 선언한 변수의 효력 범위

함수 안에서 사용할 변수의 이름을 함수 밖에서도 동일하게 사용한다면 어떻게 될까.

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

vartest(a)
print(a)
# 1

위의 함수를 돌려보면 결과값은 1이 나온다. def vartest(a)에서 입력값을 전달받는 매개변수 a는 함수 안에서만 사용하는 변수이지 함수 밖의 변수 a가 아니기 때문이다.

def vartest(a):
    a = a + 1

vartest(3)
print(a)

위 프로그램을 돌려보면 vartest()함수 안에서의 a 값은 4가 되지만 어디에도 결과값을 출력하는 코드는 없다. 따라서 print(a)를 돌리면 에러가 발생하는데 print(a)에서 입력받아야 하는 변수 a가 선언되지 않았기 때문이다. 이를 해결하기 위해서는 return을 사용하거나 global 명령어를 사용하면 된다.

# return 사용하기
a = 1 
def vartest(a): 
    a = a +1 
    return a

a = vartest(a) 
print(a)

a = vartest(a)라고 대입하면 a가 vartest 함수의 결괏값으로 바뀐다.

# global 사용하기
a = 1 
def vartest(): 
    global a 
    a = a+1

vartest() 
print(a)

vartest 함수 안의 global a 문장은 함수 안에서 함수 밖의 a 변수를 직접 사용하겠다는 뜻이다. 하지만 프로그래밍을 할 때 global 명령어는 사용하지 않는 것이 좋다. 왜냐하면 함수는 독립적으로 존재하는 것이 좋기 때문이다.

lambda 표현식

lambda는 함수를 생성할 때 사용하는 예약어로 def와 동일한 역할을 한다. 보통 함수를 한줄로 간결하게 만들 때 사용한다. def를 사용해야 할 정도로 복잡하지 않거나 def를 사용할 수 없는 곳에 주로 쓰인다. lambda 예약어로 만든 함수는 return 명령어가 없어도 결괏값을 돌려준다.

lambda 매개변수1, 매개변수2, ... : 매개변수를 이용한 표현식 형식으로 사용한다.

add = lambda a, b: a+b
result = add(3, 4)
print(result)
# 7
def add(a, b):
  return a+b

result = add(3, 4)
print(result)
# 7

이 두개는 하는 일이 완전히 동일하다.


2. 입력과 출력

입력받기

알고리즘 문제의 첫 번째 단계는 데이터를 입력받는 것이다. 파이썬에서 데이터를 입력받을 때는 input()을 사용한다. input()은 한 줄의 문자열을 입력받는다.

n = int(input())
data = list(map(int, input().split())

입력받은 데이터를 정수형 데이터로 처리하기 위해서는 문자열을 정수로 바꾸는 int() 함수를 사용한다.

input()으로 입력받은 문자열을 split()을 사용해 공백기준으로 나눈 리스트로 바꾼 후에, map()을 이용해 해당 리스트의 모든 원소에 int()함수를 적용한다. 최종적으로 그 결과를 list()로 바꿈으로써 입력받은 문자열을 띄어쓰기로 구분하여 숫자 자료형으로 저장하는 것이다.

입력의 개수가 많은 경우

문제를 풀다보면 입력을 최대한 빠르게 받아야 하는 경우가 있다. 특히, 정렬, 이진 탐색, 최단 경로 문제의 경우 매우 많은 수의 데이터가 연속적으로 입력되는데, 입력을 받는 것만으로도 시간 초과를 받을 수 있다. 따라서 파이썬에서는 sys 라이브러리에 정의되어 있는 sys.stdin.readline()함수를 사용한다.

import sys
sys.stdin.readline().rstrip()

sys.stdin.readline()는 입력후에 엔터가 줄 바꿈 기호로 입력되는데 이 공백 문자를 제거하기 위해 rstrip() 함수를 사용한다.

출력하기

파이썬의 print()는 출력 이후에 줄 바꿈을 수행한다. 따라서 한 줄에 결과값을 출력하려면 다음과 같이 end= ‘ ‘를 사용하면 된다.

for i in range(10):
  print(i, end=' ')

# 0 1 2 3 4 5 6 7 8 9

그리고 가끔 문자열과 수를 같이 출력해야 하는 경우가 있다. 이때는 str()함수로 데이터를 문자열로 바꾸어 더해주면 된다.

answer = 7

print('정답은' + str(answer) + '입니다.')
print('정답은', str(answer), '입니다.')
print(f'정답은 {answer} 입니다.')

# 정답은 7 입니다.