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