{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 25. 위치로만 인자를 지정하게 하거나 키워드로만 인자를 지정하게 해서 함수 호출을 명확하게 만들라"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "키워드 인자의 유연성을 활용하면 여러분의 코드를 처음 읽는 사람도 더 명확하게 용례를 이해할 수 있는 함수를 작성할 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def safe_division(number, divisor,\n",
    "                  ignore_overflow,\n",
    "                  ignore_zero_divison):\n",
    "    try:\n",
    "        return number / divisor\n",
    "    except OverflowError:\n",
    "        if ignore_overflow:\n",
    "            return 0\n",
    "        else:\n",
    "            raise\n",
    "    except ZeroDivisionError:\n",
    "        if ignore_zero_divison:\n",
    "            return float('inf')\n",
    "        else:\n",
    "            raise\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(1.0, 10**500, True, False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "ename": "OverflowError",
     "evalue": "int too large to convert to float",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mOverflowError\u001b[0m                             Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-11-937ebf0ae6da>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0;36m500\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mOverflowError\u001b[0m: int too large to convert to float"
     ]
    }
   ],
   "source": [
    "1.0 / 10**500"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "inf"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(1.0, 0, False, True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "문제는 어떤  예외를 무시할지 결정하는 두 불 변수의 위치를 혼동하기 쉽다는 것이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def safe_division(number, divisor,\n",
    "                  ignore_overflow=False,\n",
    "                  ignore_zero_divison=False):\n",
    "    try:\n",
    "        return number / divisor\n",
    "    except OverflowError:\n",
    "        if ignore_overflow:\n",
    "            return 0\n",
    "        else:\n",
    "            raise\n",
    "    except ZeroDivisionError:\n",
    "        if ignore_zero_divison:\n",
    "            return float('inf')\n",
    "        else:\n",
    "            raise\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "ename": "OverflowError",
     "evalue": "int too large to convert to float",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mOverflowError\u001b[0m                             Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-14-4fb969c7fd48>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msafe_division\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0;36m500\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-13-35495800f432>\u001b[0m in \u001b[0;36msafe_division\u001b[0;34m(number, divisor, ignore_overflow, ignore_zero_divison)\u001b[0m\n\u001b[1;32m      3\u001b[0m                   ignore_zero_divison=False):\n\u001b[1;32m      4\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mnumber\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mdivisor\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      6\u001b[0m     \u001b[0;32mexcept\u001b[0m \u001b[0mOverflowError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      7\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mignore_overflow\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mOverflowError\u001b[0m: int too large to convert to float"
     ]
    }
   ],
   "source": [
    "safe_division(1.0, 10**500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(1.0, 10**500, ignore_overflow=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "inf"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(1.0, 0, ignore_zero_divison=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "그러나 키워드 인자를 강요할 수 없다.\n",
    "\n",
    "이와 같이 복잡한 함수의 경우 호출자가 키워드만 사용하는 인자를 통해 의도를 명확히 밝히도록 요구하는 편이 좋다.\n",
    "\n",
    "절대 위치를 기반으로는 지정할수 없도록 한다.\n",
    "\n",
    "**\\***을 사용한다"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def safe_division(number, divisor, *,\n",
    "                  ignore_overflow=False,\n",
    "                  ignore_zero_divison=False):\n",
    "    try:\n",
    "        return number / divisor\n",
    "    except OverflowError:\n",
    "        if ignore_overflow:\n",
    "            return 0\n",
    "        else:\n",
    "            raise\n",
    "    except ZeroDivisionError:\n",
    "        if ignore_zero_divison:\n",
    "            return float('inf')\n",
    "        else:\n",
    "            raise\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "safe_division() takes 2 positional arguments but 4 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-19-bfb72866806f>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msafe_division\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0;36m500\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mFalse\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: safe_division() takes 2 positional arguments but 4 were given"
     ]
    }
   ],
   "source": [
    "safe_division(1.0, 10**500, True, False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(1.0, 10**500, ignore_overflow=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "하지만 키워드 인자와 디폴트 값은 예상대로 잘 작동한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "ename": "ZeroDivisionError",
     "evalue": "float division by zero",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-21-fec255ac0672>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msafe_division\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-17-8d972f3b7033>\u001b[0m in \u001b[0;36msafe_division\u001b[0;34m(number, divisor, ignore_overflow, ignore_zero_divison)\u001b[0m\n\u001b[1;32m      3\u001b[0m                   ignore_zero_divison=False):\n\u001b[1;32m      4\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mnumber\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mdivisor\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      6\u001b[0m     \u001b[0;32mexcept\u001b[0m \u001b[0mOverflowError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      7\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mignore_overflow\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mZeroDivisionError\u001b[0m: float division by zero"
     ]
    }
   ],
   "source": [
    "safe_division(1.0, 0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "하지만 이 함수에도 문제가 있다.\n",
    "\n",
    "이 함수의 맨 앞에 있는 두 필수 인자 (number, divisor)를 호출하면서 위치와 키워드를 혼용할 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.4"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(number=2, divisor=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.4"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(divisor=5, number=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.4"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division(2, divisor=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "def safe_division_c(numerator, denominator, *,\n",
    "                  ignore_overflow=False,\n",
    "                  ignore_zero_divison=False):\n",
    "    try:\n",
    "        return number / divisor\n",
    "    except OverflowError:\n",
    "        if ignore_overflow:\n",
    "            return 0\n",
    "        else:\n",
    "            raise\n",
    "    except ZeroDivisionError:\n",
    "        if ignore_zero_divison:\n",
    "            return float('inf')\n",
    "        else:\n",
    "            raise\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "safe_division_c() got an unexpected keyword 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-26-eea3389c34ee>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msafe_division_c\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumber\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdivisor\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5\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: safe_division_c() got an unexpected keyword argument 'number'"
     ]
    }
   ],
   "source": [
    "safe_division_c(number=2, divisor=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "파이썬 3.8에는 이 문제에 대한 해법이 있다.\n",
    "\n",
    "위치로만 지정하는 인자 라고 부른다.\n",
    "\n",
    "**/** 을 사용한다"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "def safe_division_d(numerator, denominator, /, *,\n",
    "                  ignore_overflow=False,\n",
    "                  ignore_zero_divison=False):\n",
    "    try:\n",
    "        return numerator / denominator\n",
    "    except OverflowError:\n",
    "        if ignore_overflow:\n",
    "            return 0\n",
    "        else:\n",
    "            raise\n",
    "    except ZeroDivisionError:\n",
    "        if ignore_zero_divison:\n",
    "            return float('inf')\n",
    "        else:\n",
    "            raise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.4"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division_d(2, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "safe_division_d() got some positional-only arguments passed as keyword arguments: 'numerator, denominator'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-34-ec4fa4b0805d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0msafe_division_d\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumerator\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdenominator\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m5\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: safe_division_d() got some positional-only arguments passed as keyword arguments: 'numerator, denominator'"
     ]
    }
   ],
   "source": [
    "safe_division_d(numerator=2, denominator=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "/와 \\* 기호 사이에 있는 모든 파라미터는 위치를 사용해 전달 할 수도 있고 이름을 키워드로 사용해 전달할 수도 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "def safe_division_e(numerator, denominator, /,\n",
    "                    ndigits=10, *,\n",
    "                    ignore_overflow=False,\n",
    "                    ignore_zero_divison=False):\n",
    "    try:\n",
    "        fraction = numerator / denominator\n",
    "        return round(fraction, ndigits)\n",
    "    except OverflowError:\n",
    "        if ignore_overflow:\n",
    "            return 0\n",
    "        else:\n",
    "            raise\n",
    "    except ZeroDivisionError:\n",
    "        if ignore_zero_divison:\n",
    "            return float('inf')\n",
    "        else:\n",
    "            raise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.1428571429"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division_e(22, 7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.14286"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division_e(22, 7, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.14"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "safe_division_e(22, 7, ndigits=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 기억해야 할 내용\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
}