{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 12. 스트라이드와 슬라이스를 한 식에 함께 사용하지 말라"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "파이썬은 리스트[시작:끝:증가값]으로 일정한 간격을 두고 슬라이싱을 할 수 있는 스트라이드를 제공한다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['빨강', '노랑', '파랑']\n",
      "['주황', '초록', '자주']\n"
     ]
    }
   ],
   "source": [
    "x = ['빨강', '주황', '노랑', '초록', '파랑', '자주']\n",
    "odds = x[::2]\n",
    "evens = x[1::2]\n",
    "print(odds)\n",
    "print(evens)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "스트라이드를 사용하는 구문은 버그를 야기할 수 있다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'esoognom'\n"
     ]
    }
   ],
   "source": [
    "x = b'mongoose'\n",
    "y = x[::-1]\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "司寿\n"
     ]
    }
   ],
   "source": [
    "x = '寿司'\n",
    "y = x[::-1]\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "유니코드 문자열에서도 잘 되지만 utf-8로 인코딩한 문자열에서는 작동되지 않는다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'\\xe5\\xaf\\xbf\\xe5\\x8f\\xb8'\n",
      "b'\\xb8\\x8f\\xe5\\xbf\\xaf\\xe5'\n"
     ]
    },
    {
     "ename": "UnicodeDecodeError",
     "evalue": "'utf-8' codec can't decode byte 0xb8 in position 0: invalid start byte",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mUnicodeDecodeError\u001b[0m                        Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-4-a2081e8db6c2>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0mz\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'utf-8'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xb8 in position 0: invalid start byte"
     ]
    }
   ],
   "source": [
    "w = '寿司'\n",
    "x = w.encode('utf-8')\n",
    "print(x)\n",
    "y = x[::-1]\n",
    "print(y)\n",
    "z = y.decode('utf-8')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'abcZYX123'\n",
      "b'321XYZcba'\n",
      "321XYZcba\n"
     ]
    }
   ],
   "source": [
    "w = 'abcZYX123'\n",
    "x = w.encode('utf-8')\n",
    "print(x)\n",
    "y = x[::-1]\n",
    "print(y)\n",
    "z = y.decode('utf-8')\n",
    "print(z)\n",
    "# 아스키 코드 범위에 포함되면 문제가 없음."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-1 말고 다른 음수 증가값이 유용할까?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['h', 'f', 'd', 'b']"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']\n",
    "x[::2] # ['a', 'c', 'e', 'g']\n",
    "x[::-2] # ['h', 'f', 'd', 'b']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['c', 'e', 'g']"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[2::2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['g', 'e', 'c', 'a']"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[-2::-2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['g', 'e']"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[-2:2:-2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[2:2:-2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "중요한 점은 슬라이싱 구문에 스트라이딩까지 들어가면 혼란스럽다.\n",
    "\n",
    "이런 문제를 방지하기 위해 시작값이나 끝값을 증가값과 함께 사용하지 말자."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "y = x[::2]\n",
    "z = y[1:-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "스트라이딩한 다음 슬라이싱을 하면 데이터를 한 번 더 얕게 복사하게 된다. (shallow copy)\n",
    "\n",
    "이 두 단계 연산에 필요한 시간과 메모리를 감당할 수 없다면 itertools 내장 모듈의 isliceㄷ 메서드를 고려하자."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 기억해야 할 내용\n",
    "- 슬라이스에 시작, 끝, 증가값을 함께 지정하면 코드의 의미를 혼동하기 쉽다.\n",
    "- 시작이나 끝 인덱스가 없는 슬라이스를 만들 때는 양수 증가값을 사용하라. 가급적 음수 증가값은 피하라.\n",
    "- 한 슬라이스 안에서 시작, 끝, 증가값을 함께 사용하지마라. 세 파라미터를 모두 써야하는 경우, 두번 대입을 사용하거나 itertools의 islice를 사용하라."
   ]
  }
 ],
 "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
}