{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chapter 1 — The Python Data Model\n",
    "\n",
    "**Sections with code snippets in this chapter:**\n",
    "\n",
    "* [A Pythonic Card Deck](#A-Pythonic-Card-Deck)\n",
    "* [Emulating Numeric Types](#Emulating-Numeric-Types)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A Pythonic Card Deck"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Example 1-1. A deck as a sequence of playing cards"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import collections\n",
    "\n",
    "Card = collections.namedtuple('Card', ['rank', 'suit'])\n",
    "\n",
    "class FrenchDeck:\n",
    "    ranks = [str(n) for n in range(2, 11)] + list('JQKA')\n",
    "    suits = 'spades diamonds clubs hearts'.split()\n",
    "\n",
    "    def __init__(self):\n",
    "        self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self._cards)\n",
    "\n",
    "    def __getitem__(self, position):\n",
    "        return self._cards[position]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='7', suit='diamonds')"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "beer_card = Card('7', 'diamonds')\n",
    "beer_card"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "52"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck = FrenchDeck()\n",
    "len(deck)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='2', suit='spades')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='A', suit='hearts')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='6', suit='diamonds')"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# NBVAL_IGNORE_OUTPUT\n",
    "from random import choice\n",
    "\n",
    "choice(deck)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Card(rank='2', suit='spades'),\n",
       " Card(rank='3', suit='spades'),\n",
       " Card(rank='4', suit='spades')]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[:3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Card(rank='A', suit='spades'),\n",
       " Card(rank='A', suit='diamonds'),\n",
       " Card(rank='A', suit='clubs'),\n",
       " Card(rank='A', suit='hearts')]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[12::13]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Card(rank='2', suit='spades')\n",
      "Card(rank='3', suit='spades')\n",
      "Card(rank='4', suit='spades')\n",
      "Card(rank='5', suit='spades')\n",
      "Card(rank='6', suit='spades')\n",
      "Card(rank='7', suit='spades')\n",
      "Card(rank='8', suit='spades')\n",
      "Card(rank='9', suit='spades')\n",
      "Card(rank='10', suit='spades')\n",
      "Card(rank='J', suit='spades')\n",
      "Card(rank='Q', suit='spades')\n",
      "Card(rank='K', suit='spades')\n",
      "Card(rank='A', suit='spades')\n",
      "Card(rank='2', suit='diamonds')\n",
      "Card(rank='3', suit='diamonds')\n",
      "Card(rank='4', suit='diamonds')\n",
      "Card(rank='5', suit='diamonds')\n",
      "Card(rank='6', suit='diamonds')\n",
      "Card(rank='7', suit='diamonds')\n",
      "Card(rank='8', suit='diamonds')\n",
      "Card(rank='9', suit='diamonds')\n",
      "Card(rank='10', suit='diamonds')\n",
      "Card(rank='J', suit='diamonds')\n",
      "Card(rank='Q', suit='diamonds')\n",
      "Card(rank='K', suit='diamonds')\n",
      "Card(rank='A', suit='diamonds')\n",
      "Card(rank='2', suit='clubs')\n",
      "Card(rank='3', suit='clubs')\n",
      "Card(rank='4', suit='clubs')\n",
      "Card(rank='5', suit='clubs')\n",
      "Card(rank='6', suit='clubs')\n",
      "Card(rank='7', suit='clubs')\n",
      "Card(rank='8', suit='clubs')\n",
      "Card(rank='9', suit='clubs')\n",
      "Card(rank='10', suit='clubs')\n",
      "Card(rank='J', suit='clubs')\n",
      "Card(rank='Q', suit='clubs')\n",
      "Card(rank='K', suit='clubs')\n",
      "Card(rank='A', suit='clubs')\n",
      "Card(rank='2', suit='hearts')\n",
      "Card(rank='3', suit='hearts')\n",
      "Card(rank='4', suit='hearts')\n",
      "Card(rank='5', suit='hearts')\n",
      "Card(rank='6', suit='hearts')\n",
      "Card(rank='7', suit='hearts')\n",
      "Card(rank='8', suit='hearts')\n",
      "Card(rank='9', suit='hearts')\n",
      "Card(rank='10', suit='hearts')\n",
      "Card(rank='J', suit='hearts')\n",
      "Card(rank='Q', suit='hearts')\n",
      "Card(rank='K', suit='hearts')\n",
      "Card(rank='A', suit='hearts')\n"
     ]
    }
   ],
   "source": [
    "for card in deck:\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Card(rank='A', suit='hearts')\n",
      "Card(rank='K', suit='hearts')\n",
      "Card(rank='Q', suit='hearts')\n",
      "Card(rank='J', suit='hearts')\n",
      "Card(rank='10', suit='hearts')\n",
      "Card(rank='9', suit='hearts')\n",
      "Card(rank='8', suit='hearts')\n",
      "Card(rank='7', suit='hearts')\n",
      "Card(rank='6', suit='hearts')\n",
      "Card(rank='5', suit='hearts')\n",
      "Card(rank='4', suit='hearts')\n",
      "Card(rank='3', suit='hearts')\n",
      "Card(rank='2', suit='hearts')\n",
      "Card(rank='A', suit='clubs')\n",
      "Card(rank='K', suit='clubs')\n",
      "Card(rank='Q', suit='clubs')\n",
      "Card(rank='J', suit='clubs')\n",
      "Card(rank='10', suit='clubs')\n",
      "Card(rank='9', suit='clubs')\n",
      "Card(rank='8', suit='clubs')\n",
      "Card(rank='7', suit='clubs')\n",
      "Card(rank='6', suit='clubs')\n",
      "Card(rank='5', suit='clubs')\n",
      "Card(rank='4', suit='clubs')\n",
      "Card(rank='3', suit='clubs')\n",
      "Card(rank='2', suit='clubs')\n",
      "Card(rank='A', suit='diamonds')\n",
      "Card(rank='K', suit='diamonds')\n",
      "Card(rank='Q', suit='diamonds')\n",
      "Card(rank='J', suit='diamonds')\n",
      "Card(rank='10', suit='diamonds')\n",
      "Card(rank='9', suit='diamonds')\n",
      "Card(rank='8', suit='diamonds')\n",
      "Card(rank='7', suit='diamonds')\n",
      "Card(rank='6', suit='diamonds')\n",
      "Card(rank='5', suit='diamonds')\n",
      "Card(rank='4', suit='diamonds')\n",
      "Card(rank='3', suit='diamonds')\n",
      "Card(rank='2', suit='diamonds')\n",
      "Card(rank='A', suit='spades')\n",
      "Card(rank='K', suit='spades')\n",
      "Card(rank='Q', suit='spades')\n",
      "Card(rank='J', suit='spades')\n",
      "Card(rank='10', suit='spades')\n",
      "Card(rank='9', suit='spades')\n",
      "Card(rank='8', suit='spades')\n",
      "Card(rank='7', suit='spades')\n",
      "Card(rank='6', suit='spades')\n",
      "Card(rank='5', suit='spades')\n",
      "Card(rank='4', suit='spades')\n",
      "Card(rank='3', suit='spades')\n",
      "Card(rank='2', suit='spades')\n"
     ]
    }
   ],
   "source": [
    "for card in reversed(deck):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Card('Q', 'hearts') in deck"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Card('7', 'beasts') in deck"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Card(rank='2', suit='clubs')\n",
      "Card(rank='2', suit='diamonds')\n",
      "Card(rank='2', suit='hearts')\n",
      "Card(rank='2', suit='spades')\n",
      "Card(rank='3', suit='clubs')\n",
      "Card(rank='3', suit='diamonds')\n",
      "Card(rank='3', suit='hearts')\n",
      "Card(rank='3', suit='spades')\n",
      "Card(rank='4', suit='clubs')\n",
      "Card(rank='4', suit='diamonds')\n",
      "Card(rank='4', suit='hearts')\n",
      "Card(rank='4', suit='spades')\n",
      "Card(rank='5', suit='clubs')\n",
      "Card(rank='5', suit='diamonds')\n",
      "Card(rank='5', suit='hearts')\n",
      "Card(rank='5', suit='spades')\n",
      "Card(rank='6', suit='clubs')\n",
      "Card(rank='6', suit='diamonds')\n",
      "Card(rank='6', suit='hearts')\n",
      "Card(rank='6', suit='spades')\n",
      "Card(rank='7', suit='clubs')\n",
      "Card(rank='7', suit='diamonds')\n",
      "Card(rank='7', suit='hearts')\n",
      "Card(rank='7', suit='spades')\n",
      "Card(rank='8', suit='clubs')\n",
      "Card(rank='8', suit='diamonds')\n",
      "Card(rank='8', suit='hearts')\n",
      "Card(rank='8', suit='spades')\n",
      "Card(rank='9', suit='clubs')\n",
      "Card(rank='9', suit='diamonds')\n",
      "Card(rank='9', suit='hearts')\n",
      "Card(rank='9', suit='spades')\n",
      "Card(rank='10', suit='clubs')\n",
      "Card(rank='10', suit='diamonds')\n",
      "Card(rank='10', suit='hearts')\n",
      "Card(rank='10', suit='spades')\n",
      "Card(rank='J', suit='clubs')\n",
      "Card(rank='J', suit='diamonds')\n",
      "Card(rank='J', suit='hearts')\n",
      "Card(rank='J', suit='spades')\n",
      "Card(rank='Q', suit='clubs')\n",
      "Card(rank='Q', suit='diamonds')\n",
      "Card(rank='Q', suit='hearts')\n",
      "Card(rank='Q', suit='spades')\n",
      "Card(rank='K', suit='clubs')\n",
      "Card(rank='K', suit='diamonds')\n",
      "Card(rank='K', suit='hearts')\n",
      "Card(rank='K', suit='spades')\n",
      "Card(rank='A', suit='clubs')\n",
      "Card(rank='A', suit='diamonds')\n",
      "Card(rank='A', suit='hearts')\n",
      "Card(rank='A', suit='spades')\n"
     ]
    }
   ],
   "source": [
    "suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)\n",
    "\n",
    "def spades_high(card):\n",
    "    rank_value = FrenchDeck.ranks.index(card.rank)\n",
    "    return rank_value * len(suit_values) + suit_values[card.suit]\n",
    "\n",
    "for card in sorted(deck, key=spades_high):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Emulating Numeric Types"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Example 1-2. A simple two-dimensional vector class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "class Vector:\n",
    "\n",
    "    def __init__(self, x=0, y=0):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def __repr__(self):\n",
    "        return f'Vector({self.x!r}, {self.y!r})'\n",
    "\n",
    "    def __abs__(self):\n",
    "        return math.hypot(self.x, self.y)\n",
    "\n",
    "    def __bool__(self):\n",
    "        return bool(abs(self))\n",
    "\n",
    "    def __add__(self, other):\n",
    "        x = self.x + other.x\n",
    "        y = self.y + other.y\n",
    "        return Vector(x, y)\n",
    "\n",
    "    def __mul__(self, scalar):\n",
    "        return Vector(self.x * scalar, self.y * scalar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector(4, 5)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v1 = Vector(2, 4)\n",
    "v2 = Vector(2, 1)\n",
    "v1 + v2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5.0"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v = Vector(3, 4)\n",
    "abs(v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector(9, 12)"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v * 3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15.0"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "abs(v * 3)"
   ]
  }
 ],
 "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.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
