{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 13. 슬라이싱보다는 나머지를 모두 잡아내는 언패킹을 사용하라"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "기본 언패킹(Better way 6)의 한 가지 한계점은 언패킹할 시퀀스의 길이를 미리 알고 있어야 한다는 것이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "car_ages = [0, 9, 4, 8, 7, 20, 19, 1, 6, 15]\n",
    "car_ages_descending = sorted(car_ages, reverse=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "too many values to unpack (expected 2)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-3-fd27ef4bf2c8>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0moldest\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msecond_oldest\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcar_ages_descending\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m: too many values to unpack (expected 2)"
     ]
    }
   ],
   "source": [
    "oldest, second_oldest = car_ages_descending"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "파이썬을 처음 사용하는 사람은 이런 상황에서 인덱스와 슬라이싱(Better way 11)을 자주 사용한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20 19 [15, 9, 8, 7, 6, 4, 1, 0]\n"
     ]
    }
   ],
   "source": [
    "oldest = car_ages_descending[0]\n",
    "second_oldest = car_ages_descending[1]\n",
    "others = car_ages_descending[2:]\n",
    "print(oldest, second_oldest, others)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이런 식으로 시퀀스의 원소를 여러 하위 집합으로 나누면 1 차이 나는 인덱스로 인한 오류 (off-by-one error)를 만들어내기 쉽다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이런 상황을 더 잘 다룰 수 있도록 파이썬은 **별표 식(starred expression)**을 사용해 모든 값을 담는 언패킹을 할 수 있게 지원한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20 19 [15, 9, 8, 7, 6, 4, 1, 0]\n"
     ]
    }
   ],
   "source": [
    "oldest, second_oldest, *others = car_ages_descending\n",
    "print(oldest, second_oldest, others)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이 코드는 더 짧고, 읽기 쉽고, 여러 줄 사이에 인덱스 경계 값이 어긋나서 오류가 발생할 여지도 없다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "별표 식을 다른 위치에 쓸 수도 있다. 따라서 꼭 언패킹해야만 하는 값 외에 여분의 슬라이스가 하나 필요한 경우, 나머지를 모두 잡아내는 이 기능의 이점을 살릴 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20 0 [19, 15, 9, 8, 7, 6, 4, 1]\n"
     ]
    }
   ],
   "source": [
    "oldest, *others, youngest = car_ages_descending\n",
    "print(oldest, youngest, others)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 1 [20, 19, 15, 9, 8, 7, 6, 4]\n"
     ]
    }
   ],
   "source": [
    "*others, second_youngest, youngest = car_ages_descending\n",
    "print(youngest, second_youngest, others)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "하지만 별표 식이 포함된 언패킹 대입을 처리하려면 필수인 부분이 적어도 하나는 있어야 한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "starred assignment target must be in a list or tuple (<ipython-input-8-77c6f344fe32>, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-8-77c6f344fe32>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m    *others = car_ages_descending\u001b[0m\n\u001b[0m    ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m starred assignment target must be in a list or tuple\n"
     ]
    }
   ],
   "source": [
    "*others = car_ages_descending"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "또한, 한 수준의 언패킹 패턴에 별표 식을 두 개 이상 쓸 수도 없다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "two starred expressions in assignment (<ipython-input-9-77dccc131ad1>, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-9-77dccc131ad1>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m    first, *middle, *second_middle, last = [1, 2, 3, 4]\u001b[0m\n\u001b[0m    ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m two starred expressions in assignment\n"
     ]
    }
   ],
   "source": [
    "first, *middle, *second_middle, last = [1, 2, 3, 4]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "하지만 여러 계층으로 이뤄진 구조를 언패킹할 때는 서로 다른 부분에 포함되는 한, 별표 식을 여럿 사용해도 된다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "car_inventory = {\n",
    "    '시내': ('그랜저', '아반테', '티코'),\n",
    "    '공항': ('제네시스 쿠페', '소나타', 'K5', '악센트'),\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "시내 최고는 그랜저, 나머지는 2 종\n",
      "공항 최고는 제네시스 쿠페, 나머지는 3 종\n"
     ]
    }
   ],
   "source": [
    "((loc1, (best1, *rest1)),\n",
    " (loc2, (best2, *rest2))) = car_inventory.items()\n",
    "print(f'{loc1} 최고는 {best1}, 나머지는 {len(rest1)} 종')\n",
    "print(f'{loc2} 최고는 {best2}, 나머지는 {len(rest2)} 종')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "별포 식은 항상 list 인스턴스가 된다. 빈 리스트가 될 수도 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 []\n"
     ]
    }
   ],
   "source": [
    "short_list = [1, 2]\n",
    "first, second, *rest = short_list\n",
    "print(first, second, rest)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "언패킹 구문을 사용해 임의의 이터레이터를 가져올 수도 있지만 기본 다중 대입문보다 그다지 많이 쓸모 있지는 않다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "다음 예제는 길이가 2인 range에 들어 있는 값을 언패킹한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 & 2\n"
     ]
    }
   ],
   "source": [
    "it = iter(range(1, 3))\n",
    "first, second = it\n",
    "print(f'{first} & {second}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "하지만 별표 식을 추가하면 언패킹할 이터레이터의 값을 깔끔하게 가져올 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_csv():\n",
    "    yield ('날짜', '제조사' , '모델', '연식', '가격')\n",
    "    for i in range(100):\n",
    "        yield ('2019-03-25', '현대', '소나타', '2010', '2400만원')\n",
    "        yield ('2019-03-26', '기아', '프라이드', '2008', '1400만원')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이 제너레이터를 인덱스와 슬라이스를 이용하여 처리할 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CSV 헤더: ('날짜', '제조사', '모델', '연식', '가격')\n",
      "행 수:  200\n",
      "('2019-03-25', '현대', '소나타', '2010', '2400만원')\n",
      "('2019-03-26', '기아', '프라이드', '2008', '1400만원')\n",
      "('2019-03-25', '현대', '소나타', '2010', '2400만원')\n"
     ]
    }
   ],
   "source": [
    "all_csv_rows = list(generate_csv())\n",
    "header = all_csv_rows[0]\n",
    "rows = all_csv_rows[1:]\n",
    "print('CSV 헤더:', header)\n",
    "print('행 수: ', len(rows))\n",
    "print(rows[0])\n",
    "print(rows[1])\n",
    "print(rows[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "별표 식으로 언패킹하면 이터레이터가 내보내는 내용 중에 첫 번째와 나머지를 쉽게 나눠서 처리할 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CSV 헤더: ('날짜', '제조사', '모델', '연식', '가격')\n",
      "행 수:  200\n"
     ]
    }
   ],
   "source": [
    "it = generate_csv()\n",
    "header, *rows = it\n",
    "print('CSV 헤더:', header)\n",
    "print('행 수: ', len(rows))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('2019-03-25', '현대', '소나타', '2010', '2400만원')\n",
      "('2019-03-26', '기아', '프라이드', '2008', '1400만원')\n",
      "('2019-03-25', '현대', '소나타', '2010', '2400만원')\n"
     ]
    }
   ],
   "source": [
    "print(rows[0])\n",
    "print(rows[1])\n",
    "print(rows[2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "하지만 별표 식은 항상 리스트를 만들어내기 때문에 이터레이터를 별표식으로 언패킹하면 컴퓨터 메모리를 모두 다 사용해서 프로그램이 멈출 수 있다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  기억해야 할 내용\n",
    "- 언패킹 대입에 별표 식을 사용하면 언패킹 패턴에서 대입되지 않는 모든 부분을 리스트에 잡아 낼 수 있다.\n",
    "- 별표 식은 언패킹 패턴의 어떤 위치에든 놓을 수 있다. 별표 식에 대입된 결과는 항상 리스트가 되며, 이 리스트에는 별표 식이 받은 값이 0개 또는 그 이상이 들어간다.\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
}