{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 22. 변수 위치 인자를 사용해 시각적인 잡음을 줄여라"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "위치 인자 (Positional argument) 를 가변적으로 받을 수 있으면 함수 호출이 더 깔끔해지고 시각적 잡음도 줄어든다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def log(message, values):\n",
    "    if not values:\n",
    "        print(message)\n",
    "    else:\n",
    "        values_str = ', '.join(str(x) for x in values)\n",
    "        print(f'{message}: {values_str}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "내 숫자는: 1, 2\n"
     ]
    }
   ],
   "source": [
    "log('내 숫자는', [1, 2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "안녕\n"
     ]
    }
   ],
   "source": [
    "log('안녕', [])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "빈 리스트를 넘겨야 한다면 귀찮음 뿐만 아니라 잡음이 많다.\n",
    "\n",
    "생략 가능하도록 하자"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def log(message, *values):\n",
    "    if not values:\n",
    "        print(message)\n",
    "    else:\n",
    "        values_str = ', '.join(str(x) for x in values)\n",
    "        print(f'{message}: {values_str}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "내 숫자는: 1, 2\n"
     ]
    }
   ],
   "source": [
    "log('내 숫자는', 1, 2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "안녕\n"
     ]
    }
   ],
   "source": [
    "log('안녕')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "언패킹 대입문에 쓰인 별표식과 비슷함\n",
    "\n",
    "시퀀스를 사용하고 싶다면 * 연산자를 사용하면 된다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "내 숫자는: 1, 2, 3\n"
     ]
    }
   ],
   "source": [
    "favorite = [1, 2, 3]\n",
    "log('내 숫자는', *favorite)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "내 숫자는: [1, 2, 3]\n"
     ]
    }
   ],
   "source": [
    "favorite = [1, 2, 3]\n",
    "log('내 숫자는', favorite)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "가변적인 위치 인자를 받는 데는 두 가지 문제점이 있다.\n",
    "\n",
    "첫번째 : 선택적인 위치 인자가 함수에 전달되기 전에 항상 튜플로 변환됨\n",
    "\n",
    "메모리를 아주 많이 소비하거나 프로그램이 중단될 수 있음."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_generator():\n",
    "    for i in range(10):\n",
    "        yield i"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_func(*args):\n",
    "    print(args)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "it = my_generator()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)\n"
     ]
    }
   ],
   "source": [
    "my_func(*it)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "두 번째 문제점은 새로운 위치 인자를 추가하면 해당 함수를 호출하는 모든 코드를 변경해야 한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "def log(sequence, message, *values):\n",
    "    if not values:\n",
    "        print(f'{sequence} - {message}')\n",
    "    else:\n",
    "        values_str = ', '.join(str(x) for x in values)\n",
    "        print(f'{sequence} - {message}: {values_str}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 - 좋아하는 숫자는: 7, 33\n"
     ]
    }
   ],
   "source": [
    "log(1, '좋아하는 숫자는', 7, 33)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 - 안녕\n"
     ]
    }
   ],
   "source": [
    "log(1, '안녕')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "좋아하는 숫자는 - 7: 33\n"
     ]
    }
   ],
   "source": [
    "log('좋아하는 숫자는', 7, 33)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이런 가능성을 완전히 없애려면 \\*args를 받아들이는 함수를 확장할 떄는 키워드 기반의 인자만 사용해야 한다. (Better way 25)\n",
    "\n",
    "더 방어적으로 프로그래밍하려면 타입 애너테이션 (Better way 90)을 사용해도 된다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 기억해야 할 내용\n",
    "- def 문에서 \\*args를 사용하면 함수가 가변 위치 인자를 받을 수 있다.\n",
    "- \\* 연산자를 사용하면 가변 인자를 받는 함수에게 시퀀스 내의 원소들을 전달할 수 있다.\n",
    "- 제너레이터에 \\* 연산자를 사용하면 프로그램이 메모리를 모두 소진하고 중단될 수 있다.\n",
    "- \\*args를 받는 함수에 새로운 위치 기반 인자를 넣으면 감지하기 힘든 버그가 생길 수 있다."
   ]
  }
 ],
 "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
}