이 노트북은 제이크 반더플라스(Jake VanderPlas)의 A Whirlwind Tour of Python(OReilly Media, 2016)를 기반으로 만들어졌습니다. 이 내용은 CC0 라이센스를 따릅니다. 전체 노트북의 목록은 https://github.com/rickiepark/WhirlwindTourOfPython 에서 볼 수 있습니다.
지금까지는 간단하고 일회성 코드 블럭을 만들었습니다.
파이썬 코드를 조직하여 가독성과 재사용성을 높이는 한가지 방법은 유용한 코드 일부를 재사용 가능한 함수로 뽑아내는 것입니다.
여기서는 함수를 만드는 두 가지 방법을 다룹니다. 어떤 종류의 함수도 만들 수 있는 def
문과 짧고 익명의 함수를 만드는 lambda
문입니다.
함수는 이름을 가진 코드 묶음이며 소괄를 사용하여 호출됩니다.
이미 함수를 보았습니다. 예르 들어, 파이썬 3에서 print
는 함수입니다:
print('abc')
abc
여기에서 print
는 함수 이름이고 'abc'
는 함수의 매개변수입니다.
매개변수에 이름을 지정할 수 있는 키워드 매개변수도 있습니다.
print()
함수에 있는 (파이썬 3에서) 유일한 키워드 매개변수는 sep
입니다. 이 매개변수는 여러개의 아이템을 구분할 때 사용할 문자나 문자열을 지정합니다:
print(1, 2, 3)
1 2 3
print(1, 2, 3, sep='--')
1--2--3
일반 매개변수와 키워드 매개변수가 함께 쓰일 때 키워드 매개변수는 맨 뒤에 위치해야 합니다.
여러 곳에서 사용되는 기능을 조직화하여 자신만의 함수를 정의할 때 함수의 가치가 빛을 발합니다.
파이썬에서 함수는 def
문으로 정의합니다.
예를 들어, 이전 섹션에서 피보나치 수열 코드를 다음과 같이 함수로 만들 수 있습니다:
def fibonacci(N):
L = []
a, b = 0, 1
while len(L) < N:
a, b = b, a + b
L.append(a)
return L
이제 매개변수 N
하나를 가지고 무언가를 하는 fibonacci
이름의 함수 하나가 만들어졌습니다. return
은 값을 반환합니다. 여기에서는 N
개의 피보나치 수열의 리스트를 반환합니다:
fibonacci(10)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
C
와 같은 고정 타입의 언어에 익숙하다면 함수의 입력과 출력에 연관된 타입 정보가 없다는 것을 눈치챘을 것입니다.
파이썬의 함수는 간단하거나 복합적인 어떤 파이썬 오브젝트도 반환할 수 있습니다. 다른 언어에서는 어려울 수 있는 작업이 파이썬에서는 간단합니다.
예를 들면, 콤마로 구분하여 여러 값을 반환하면 하나의 튜플로 연결됩니다:
def real_imag_conj(val):
return val.real, val.imag, val.conjugate()
r, i, c = real_imag_conj(3 + 4j)
print(r, i, c)
3.0 4.0 (3-4j)
종종 함수를 정의할 때 주로 사용했으면 하는 어떤 값이 있습니다. 또 사용자가 바꿀 수도 있어야 합니다.
이런 경우에 매개변수에 기본값을 지정할 수 있습니다.
이전의 fibonacci
함수를 생각해 보죠.
사용자에게 시작 값을 정할 수 있게 하면 어떨까요?
다음과 같이 만들 수 있습니다:
def fibonacci(N, a=0, b=1):
L = []
while len(L) < N:
a, b = b, a + b
L.append(a)
return L
하나의 매개변수만 사용하면 함수의 결과는 이전과 동일합니다:
fibonacci(10)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
하지만 시작값 등을 바꾸어 사용할 수 있습니다:
fibonacci(10, 0, 2)
[2, 2, 4, 6, 10, 16, 26, 42, 68, 110]
키워드 매개변수 순서가 다를 경우 이름과 함께 값을 지정할 수 있습니다:
fibonacci(10, b=3, a=1)
[3, 4, 7, 11, 18, 29, 47, 76, 123, 199]
*args
와 **kwargs
: 유연한 매개변수¶이따금 사용자가 얼마나 많은 매개변수를 전달할지 알 수 없는 함수를 만들어야 할 때가 있습니다.
이런 경우에 전달되는 모든 매개변수를 담을 수 있는 특별한 형태인 *args
와 **kwargs
를 사용할 수 있습니다.
다음 예를 보겠습니다:
def catch_all(*args, **kwargs):
print("args =", args)
print("kwargs = ", kwargs)
catch_all(1, 2, 3, a=4, b=5)
args = (1, 2, 3) kwargs = {'a': 4, 'b': 5}
catch_all('a', keyword=2)
args = ('a',) kwargs = {'keyword': 2}
args
와 kwargs
이름이 중요한 것이 아니라 앞에 붙은 *
문자가 중요합니다.
args
와 kwargs
는 관례적으로 자주 사용하는 이름으로 "arguments"와 "keyword arguments"의 약자입니다.
작동 방식은 *
문자로 결정됩니다. 변수 앞에 하나의 *
가 있으면 시퀀스로 확장하라는 뜻이고, 변수 앞에 두 개의 *
가 있으면 딕셔너리로 확장하라는 뜻입니다.
사실 이 문법은 함수 정의 뿐만 아니라 함수 호출에서도 사용할 수 있습니다!
inputs = (1, 2, 3)
keywords = {'pi': 3.14}
catch_all(*inputs, **keywords)
args = (1, 2, 3) kwargs = {'pi': 3.14}
print(inputs)
(1, 2, 3)
print(*inputs)
1 2 3
lambda
) 함수¶앞에서 함수를 정의하는 가장 일반적인 방법인 def
문을 다루었습니다.
다른 방법으로 짧고 일회성 함수를 정의하는 lambda
문이 있습니다.
다음과 같습니다:
add = lambda x, y: x + y
add(1, 2)
3
이 람다 함수는 다음과 거의 동일합니다.
def add(x, y):
return x + y
왜 이런 함수가 필요한 걸까요? 기본적으로 파이썬에서 모든 것이 객체이기 때문에 발생합니다. 심지어 함수도 객체입니다! 이 말은 함수의 매개변수로 함수를 전달할 수 있다는 뜻입니다.
예제를 위해서 딕셔너리의 리스트를 만들어 보겠습니다:
data = [{'first':'Guido', 'last':'Van Rossum', 'YOB':1956},
{'first':'Grace', 'last':'Hopper', 'YOB':1906},
{'first':'Alan', 'last':'Turing', 'YOB':1912}]
이제 이 데이터를 정렬해보겠습니다. 파이썬은 sorted
함수를 제공합니다:
sorted([2,4,3,5,1,6])
[1, 2, 3, 4, 5, 6]
하지만 딕셔너리는 순서가 없습니다. 어떻게 데이터를 정렬할지 알려 주어야 합니다.
key
매개변수에 아이템을 정렬할 키를 반환해 주는 함수를 지정하여 해결할 수 있습니다:
# 'first'를 기준으로 알파벳 순으로 정렬합니다
sorted(data, key=lambda item: item['first'])
[{'first': 'Alan', 'last': 'Turing', 'YOB': 1912}, {'first': 'Grace', 'last': 'Hopper', 'YOB': 1906}, {'first': 'Guido', 'last': 'Van Rossum', 'YOB': 1956}]
# 생년을 기준으로 정렬합니다
sorted(data, key=lambda item: item['YOB'])
[{'first': 'Grace', 'last': 'Hopper', 'YOB': 1906}, {'first': 'Alan', 'last': 'Turing', 'YOB': 1912}, {'first': 'Guido', 'last': 'Van Rossum', 'YOB': 1956}]
이런 키 함수를 보통의 def
문으로 생성하여 사용할 수 있지만 lambda
문을 사용해 일회용 함수를 사용하는게 편리합니다.