{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 23. 키워드 인자로 선택적인 기능을 제공하라"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "함수를 호출할때 위치에 따라 인자를 넘길 수 있다"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "def remainder(number, divisor):\n",
    "    return number % divisor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert remainder(20, 7) == 6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "remainder(20, 7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "remainder(20, divisor=7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "remainder(number=20, divisor=7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "remainder(divisor=7, number=20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "positional argument follows keyword argument (<ipython-input-7-9265fd4030d2>, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-7-9265fd4030d2>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m    remainder(number=20, 7)\u001b[0m\n\u001b[0m                         ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n"
     ]
    }
   ],
   "source": [
    "remainder(number=20, 7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "remainder() got multiple values for argument 'number'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-8-f2d03bd9e009>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mremainder\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m7\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: remainder() got multiple values for argument 'number'"
     ]
    }
   ],
   "source": [
    "remainder(20, number=7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "딕셔너리의 내용물을 사용해 remainder와 같은 함수를 호출하고 싶다면 \\*\\*연산자를 사용할 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_kwargs = {\n",
    "    'number': 20,\n",
    "    'divisor': 7\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert remainder(**my_kwargs) == 6"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\\*\\* 연산자를 위치 인자나 키워드 인자와 섞어서 함수를 호출할 수 있다. 다만 중복되는 인자가 없어야 한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_kwargs = {\n",
    "    'divisor': 7,\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert remainder(number=20, **my_kwargs) == 6"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\\*\\* 연산자를 여러 번 사용할 수도 있다. 다만 여러 딕셔너리에 겹치는 키가 없어야 한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_kwargs = {\n",
    "    'number': 20,\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "other_kwargs = {\n",
    "    'divisor': 7,\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "assert remainder(**my_kwagrs, **other_kwargs) == 6"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "아무 키워드 인자나 받는 함수를 만들고 싶다면, 모든 키워드 인자를 dict에 모아주는 \\*\\*kwargs 라는 파라미터를 사용한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print_parameters(**kwargs):\n",
    "    for key, value in kwargs.items():\n",
    "        print(f'{key} = {value}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "alpha = 1.5\n",
      "beta = 9\n",
      "감마 = 4\n"
     ]
    }
   ],
   "source": [
    "print_parameters(alpha=1.5, beta=9, 감마=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "키워드 인자가 제공하는 세가지 이점\n",
    "1. 코드를 처음 보는 사람들에게 함수 호출의 의미를 명확히 알려줄 수 있다.\n",
    "2. 함수 정의에서 디폴트 값을 지정할 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def flow_rate(weight_Diff, time_diff):\n",
    "    return weight_diff / time_diff"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "weight_diff = 0.5\n",
    "time_diff = 3\n",
    "flow = flow_rate(weight_diff, time_diff)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.167 kg/s\n"
     ]
    }
   ],
   "source": [
    "print(f'{flow:.3} kg/s')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "def flow_rate(weight_Diff, time_diff, period):\n",
    "    return (weight_diff / time_diff) * period"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "flow_per_second = flow_rate(weight_diff, time_diff, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.16666666666666666"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "flow_per_second"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "def flow_rate(weight_Diff, time_diff, period=1):\n",
    "    return (weight_diff / time_diff) * period"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "flow_per_second = flow_rate(weight_diff, time_diff)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "flow_per_hour = flow_rate(weight_diff, time_diff, period=3600)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3. 어떤 함수를 사용하던 기존 호출자에게는 하위 호환성을 제공하면서 함수 파라미터를 확장할 수 있는 방법을 제공한다는 것이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "def flow_rate(weight_Diff, time_diff, period=1, units_per_kg=1):\n",
    "    return ((weight_diff / units_per_kg) / time_diff) * period"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "flow_per_hour = flow_rate(weight_diff, time_diff, period=3600, units_per_kg=2.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "272.72727272727275"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "flow_per_hour"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "선택적 인자를 위치인자로 지정하면 혼동을 야기할 수 있다.\n",
    "\n",
    "따라서 항상 키워드 인자를 사용하고 위치 인자를 절대 사용하지 않는 것"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 기억해야 할 내용\n",
    "- 함수 인자를 위치에 따라 지정할 수도 있고, 키워드를 사용해 지정할 수도 있다.\n",
    "- 키워드를 사용하면 위치 인자만 사용할 떄는 혼동할 수 있는 여러 인자의 목적을 명확히 할 수 있다.\n",
    "- 키워드 인자와 디폴트 값을 함께 사용하면 기본 호출 코드를 마이그레이션하지 않고도 함수에 새로운 기능을 쉽게 추가할 수 있다.\n",
    "- 선택적 키워드 인자는 항상 위치가 아니라 키워드를 사용해 전달돼야 한다."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}