{ "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 }