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