{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 6. 인덱스를 사용하는 대신 대입을 사용해 데이터를 언패킹하라"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "파이썬에는 값으로 이뤄진 불변(immutable) 순서쌍을 만들어낼 수 있는 tuple 내장타입이 있다.\n",
    "\n",
    "가장 짧은 튜플은 딕셔너리의 키-값 쌍과 비슷하게 두 값으로 이뤄진다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "snack_calories = {\n",
    "    '감자칩': 140,\n",
    "    '팝콘': 80,\n",
    "    '땅콩': 190,\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'감자칩': 140, '팝콘': 80, '땅콩': 190}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "snack_calories"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(('감자칩', 140), ('팝콘', 80), ('땅콩', 190))\n"
     ]
    }
   ],
   "source": [
    "items = tuple(snack_calories.items())\n",
    "print(items)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "item = ('호박엿', '식혜')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "호박엿 & 식혜\n"
     ]
    }
   ],
   "source": [
    "first = item[0]\n",
    "second = item[1]\n",
    "print(first, '&', second)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "튜플은 인덱스를 통해 변경 불가능 (immutable)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'tuple' object does not support item assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-6-a813456798f0>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mpair\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m'약과'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'호박엿'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mpair\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[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment"
     ]
    }
   ],
   "source": [
    "pair = ('약과', '호박엿')\n",
    "pair[0] = '타래과'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "파이썬에는 **언패킹** 구문이 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "호박엿 & 식혜\n"
     ]
    }
   ],
   "source": [
    "item = ('호박엿', '식혜')\n",
    "first, second = item # 언퍀킹\n",
    "print(first, '&', second)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "인덱스 사용보다 시각적인 잡음이 적고, 이터러블이 들어간 경우 등 다양한 패턴을 언패킹 구문에 사용할 수 있다.\n",
    "\n",
    "다음과 같은 코드는 추천하지는 않지만 가능하다는 사실을 알고 있는 것도 중요하다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "favorite_snacks = {\n",
    "    '짭조름한 과자': ('프레즐', 100),\n",
    "    '달콤한 과자': ('쿠키', 180),\n",
    "    '채소': ('당근', 20),\n",
    "}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "((type1, (name1, cals1)),\n",
    " (type2, (name2, cals2)),\n",
    " (type3, (name3, cals3))) = favorite_snacks.items()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "제일 좋아하는 짭조름한 과자 는 프레즐, 100 칼로리입니다.\n",
      "제일 좋아하는 달콤한 과자 는 쿠키, 180 칼로리입니다.\n",
      "제일 좋아하는 채소 는 당근, 20 칼로리입니다.\n"
     ]
    }
   ],
   "source": [
    "print(f'제일 좋아하는 {type1} 는 {name1}, {cals1} 칼로리입니다.')\n",
    "print(f'제일 좋아하는 {type2} 는 {name2}, {cals2} 칼로리입니다.')\n",
    "print(f'제일 좋아하는 {type3} 는 {name3}, {cals3} 칼로리입니다.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "언패킹을 사용하면 임시 변수를 정의하지 않고도 값을 맞바꿀 수 있다.\n",
    "\n",
    "다음은 오름차군 정렬 알고리즘이다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def bubble_sort(a):\n",
    "    for _ in range(len(a)):\n",
    "        for i in range(1, len(a)):\n",
    "            if a[i] < a[i-1]:\n",
    "                temp = a[i]\n",
    "                a[i] = a[i-1]\n",
    "                a[i-1] = temp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "names = ['프레즐', '당근', '쑥갓', '베이컨']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "bubble_sort(names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['당근', '베이컨', '쑥갓', '프레즐']\n"
     ]
    }
   ],
   "source": [
    "print(names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "언패킹 사용"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def bubble_sort(a):\n",
    "    for _ in range(len(a)):\n",
    "        for i in range(1, len(a)):\n",
    "            if a[i] < a[i-1]:\n",
    "                a[i-1], a[i] = a[i], a[i-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['당근', '베이컨', '쑥갓', '프레즐']\n"
     ]
    }
   ],
   "source": [
    "names = ['프레즐', '당근', '쑥갓', '베이컨']\n",
    "bubble_sort(names)\n",
    "print(names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "우항 값이 임시 tuple에 들어가고 좌항에 임시 tuple의 값을 대입한다.\n",
    "\n",
    "그 후 임시 tuple은 사라진다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "언패킹의 용례 중에서 또 한 가지 쓸모 있는 것으로 for 루프 또는 그와 비슷한 다른 요소(컴프리헨션이나 제너레이터 식)의 대상인 리스트의 원소를 언패킹하는 것이 있다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 언패킹 X"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "#1: 베이컨 은 350 칼로리 입니다.\n",
      "#2: 도넛 은 240 칼로리 입니다.\n",
      "#3: 머핀 은 190 칼로리 입니다.\n"
     ]
    }
   ],
   "source": [
    "snacks = [('베이컨', 350), ('도넛', 240), ('머핀', 190)]\n",
    "for i in range(len(snacks)):\n",
    "    item = snacks[i]\n",
    "    name = item[0]\n",
    "    calories = item[1]\n",
    "    print(f'#{i+1}: {name} 은 {calories} 칼로리 입니다.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 언패킹 O"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "#1: 베이컨 은 350 칼로리입니다.\n",
      "#2: 도넛 은 240 칼로리입니다.\n",
      "#3: 머핀 은 190 칼로리입니다.\n"
     ]
    }
   ],
   "source": [
    "for rank, (name, calories) in enumerate(snacks, 1):\n",
    "    print(f'#{rank}: {name} 은 {calories} 칼로리입니다.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "pythonic한 방식이다."
   ]
  },
  {
   "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
}