Full Code of fluentpython/notebooks for AI

master 4b033e4bc162 cached
19 files
56.3 KB
18.5k tokens
17 symbols
1 requests
Download .txt
Repository: fluentpython/notebooks
Branch: master
Commit: 4b033e4bc162
Files: 19
Total size: 56.3 KB

Directory structure:
gitextract_97iked2r/

├── .gitignore
├── 01-data-model/
│   ├── README.rst
│   ├── frenchdeck.doctest
│   ├── frenchdeck.ipynb
│   ├── frenchdeck.py
│   ├── frenchdeck_soln.ipynb
│   ├── vector2d.ipynb
│   ├── vector2d.py
│   └── vector2d_soln.ipynb
├── 04-text-byte/
│   ├── README.rst
│   ├── asciize.ipynb
│   ├── asciize.py
│   ├── default_encodings.py
│   ├── normeq.py
│   ├── numerics_demo.py
│   ├── ola.py
│   └── ramanujan.py
├── LICENSE
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================

*.sublime-project
*.sublime-workspace
.ipynb_checkpoints/
.py311/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Java
*.class

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# PyCharm
.idea/


================================================
FILE: 01-data-model/README.rst
================================================
Sample code for Chapter 1 - "The Python Data Model"

From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
http://shop.oreilly.com/product/0636920032519.do


================================================
FILE: 01-data-model/frenchdeck.doctest
================================================
>>> from frenchdeck import FrenchDeck, Card
>>> beer_card = Card('7', 'diamonds')
>>> beer_card
Card(rank='7', suit='diamonds')
>>> deck = FrenchDeck()
>>> len(deck)
52
>>> deck[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
>>> deck[12::13]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
>>> Card('Q', 'hearts') in deck
True
>>> Card('Z', 'clubs') in deck
False
>>> for card in deck:  # doctest: +ELLIPSIS
...   print(card)
Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...
>>> for card in reversed(deck):  # doctest: +ELLIPSIS
...   print(card)
Card(rank='A', suit='hearts')
Card(rank='K', suit='hearts')
Card(rank='Q', suit='hearts')
...
>>> for n, card in enumerate(deck, 1):  # doctest: +ELLIPSIS
...   print(n, card)
1 Card(rank='2', suit='spades')
2 Card(rank='3', suit='spades')
3 Card(rank='4', suit='spades')
...
>>> suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
>>> def spades_high(card):
...     rank_value = FrenchDeck.ranks.index(card.rank)
...     return rank_value * len(suit_values) + suit_values[card.suit]

Rank test:

>>> spades_high(Card('2', 'clubs'))
0
>>> spades_high(Card('A', 'spades'))
51

>>> for card in sorted(deck, key=spades_high):  # doctest: +ELLIPSIS
...      print(card)
Card(rank='2', suit='clubs')
Card(rank='2', suit='diamonds')
Card(rank='2', suit='hearts')
...
Card(rank='A', suit='diamonds')
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')


================================================
FILE: 01-data-model/frenchdeck.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8683f614",
   "metadata": {},
   "source": [
    "### Card and Deck objects\n",
    "\n",
    "This notebook contains example code from [*Fluent Python*](http://shop.oreilly.com/product/0636920032519.do), by Luciano Ramalho.\n",
    "\n",
    "Code by Luciano Ramalho, modified by Allen Downey.\n",
    "\n",
    "MIT License: https://opensource.org/licenses/MIT"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a301e6ce",
   "metadata": {},
   "source": [
    "This example demonstrates the Python data model using a simple implementation of playing cards and decks.\n",
    "\n",
    "`Card` is a namedtuple that represents a playing card."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "60d128c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import collections\n",
    "\n",
    "Card = collections.namedtuple('Card', ['rank', 'suit'])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93253972",
   "metadata": {},
   "source": [
    "`FrenchDeck` is a class that represents a deck of cards."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "12ee5e3f",
   "metadata": {},
   "outputs": [],
   "source": [
    "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\n",
    "                                        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": "markdown",
   "id": "612a5505",
   "metadata": {},
   "source": [
    "You can instantiate a `Card` object as if `Card` were a class.\n",
    "\n",
    "BTW: [beer card](https://en.wikipedia.org/wiki/Beer_card)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f840e176",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='7', suit='diamonds')"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "beer_card = Card('7', 'diamonds')\n",
    "beer_card"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4ac7f593",
   "metadata": {},
   "source": [
    "You can access the fields of a card by name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "4e9cf489",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('7', 'diamonds')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "beer_card.rank, beer_card.suit"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d8f420b",
   "metadata": {},
   "source": [
    "Or by index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "087eb2af",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('7', 'diamonds')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "beer_card[0], beer_card[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d5f4a31c",
   "metadata": {},
   "source": [
    "A drawback of using namedtuples is that you can't define methods for them in the usual way.\n",
    "\n",
    "But you can [monkey-patch](https://en.wikipedia.org/wiki/Monkey_patch) them by defining a function and then making it an attribute of `Card`.  For example, here's a function that generates a string representation of a card:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d98ced9f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'7 of diamonds'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def card_to_str(card):\n",
    "    return '%s of %s' % card\n",
    "\n",
    "card_to_str(beer_card)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68ac5d52",
   "metadata": {},
   "source": [
    "Here's how we can make that function behave like a method.  When we pass a card to `print`, Python invokes the special method `__str__`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "93100815",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7 of diamonds\n"
     ]
    }
   ],
   "source": [
    "Card.__str__ = card_to_str\n",
    "print(beer_card)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c661f3e5",
   "metadata": {},
   "source": [
    "Now let's instantiate a `FrenchDeck`.\n",
    "\n",
    "When we call `len`, Python invokes the `__len__` method on the deck. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "585d44bf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "52"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck = FrenchDeck()\n",
    "len(deck)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e39f095",
   "metadata": {},
   "source": [
    "When we use the bracket operator, Python invokes the `__getitem__` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "13a6f6df",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='5', suit='spades')"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3987de6a",
   "metadata": {},
   "source": [
    "And that means that the slice operator works, too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "5ebb8717",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Card(rank='2', suit='spades'),\n",
       " Card(rank='3', suit='spades'),\n",
       " Card(rank='4', suit='spades')]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deck[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8436455b",
   "metadata": {},
   "source": [
    "Aside: In this context, we don't get the string generated by `__str__`; we get the one generated by `__repr__` (read about that [here](https://docs.python.org/3/reference/datamodel.html#basic-customization)) \n",
    "\n",
    "Because `FrenchDeck` provides `__len__` and `__getitem__`, it is considered a sequence, which means that the `in` operator works:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9cd61a8c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Card('Q', 'hearts') in deck"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dcd2565",
   "metadata": {},
   "source": [
    "**Exercise** Make up a card that doesn't exist and confirm that `in` returns `False`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "a22587e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20cec6b8",
   "metadata": {},
   "source": [
    "And the for loop works, too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "c0dc0afc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2 of spades\n",
      "3 of spades\n",
      "4 of spades\n",
      "5 of spades\n",
      "6 of spades\n",
      "7 of spades\n",
      "8 of spades\n",
      "9 of spades\n",
      "10 of spades\n",
      "J of spades\n",
      "Q of spades\n",
      "K of spades\n",
      "A of spades\n",
      "2 of diamonds\n",
      "3 of diamonds\n",
      "4 of diamonds\n",
      "5 of diamonds\n",
      "6 of diamonds\n",
      "7 of diamonds\n",
      "8 of diamonds\n",
      "9 of diamonds\n",
      "10 of diamonds\n",
      "J of diamonds\n",
      "Q of diamonds\n",
      "K of diamonds\n",
      "A of diamonds\n",
      "2 of clubs\n",
      "3 of clubs\n",
      "4 of clubs\n",
      "5 of clubs\n",
      "6 of clubs\n",
      "7 of clubs\n",
      "8 of clubs\n",
      "9 of clubs\n",
      "10 of clubs\n",
      "J of clubs\n",
      "Q of clubs\n",
      "K of clubs\n",
      "A of clubs\n",
      "2 of hearts\n",
      "3 of hearts\n",
      "4 of hearts\n",
      "5 of hearts\n",
      "6 of hearts\n",
      "7 of hearts\n",
      "8 of hearts\n",
      "9 of hearts\n",
      "10 of hearts\n",
      "J of hearts\n",
      "Q of hearts\n",
      "K of hearts\n",
      "A of hearts\n"
     ]
    }
   ],
   "source": [
    "for card in deck:\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd516e84",
   "metadata": {},
   "source": [
    "Other methods that work with sequences, like `random.choice`, will work with decks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "6769a93c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Card(rank='3', suit='hearts')"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from random import choice\n",
    "choice(deck)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4607555",
   "metadata": {},
   "source": [
    "Sadly, `shuffle` doesn't work because we haven't provided `__setitem__`, so a deck is an immutable sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "532acbb6",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'FrenchDeck' 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)",
      "Cell \u001b[0;32mIn[15], line 4\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mrandom\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m shuffle\n\u001b[1;32m      3\u001b[0m \u001b[38;5;66;03m# This should raise a TypeError\u001b[39;00m\n\u001b[0;32m----> 4\u001b[0m \u001b[43mshuffle\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdeck\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/random.py:380\u001b[0m, in \u001b[0;36mRandom.shuffle\u001b[0;34m(self, x)\u001b[0m\n\u001b[1;32m    377\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mreversed\u001b[39m(\u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m1\u001b[39m, \u001b[38;5;28mlen\u001b[39m(x))):\n\u001b[1;32m    378\u001b[0m     \u001b[38;5;66;03m# pick an element in x[:i+1] with which to exchange x[i]\u001b[39;00m\n\u001b[1;32m    379\u001b[0m     j \u001b[38;5;241m=\u001b[39m randbelow(i \u001b[38;5;241m+\u001b[39m \u001b[38;5;241m1\u001b[39m)\n\u001b[0;32m--> 380\u001b[0m     \u001b[43mx\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m, x[j] \u001b[38;5;241m=\u001b[39m x[j], x[i]\n",
      "\u001b[0;31mTypeError\u001b[0m: 'FrenchDeck' object does not support item assignment"
     ]
    }
   ],
   "source": [
    "from random import shuffle\n",
    "\n",
    "# This should raise a TypeError\n",
    "shuffle(deck)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1a86bf8e",
   "metadata": {},
   "source": [
    "We can use `sorted` to iterate through the cards in the order determined by tuple comparison:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6190c00d",
   "metadata": {},
   "outputs": [],
   "source": [
    "for card in sorted(deck):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42bac4c6",
   "metadata": {},
   "source": [
    "If we want an ordering that makes more sense for cards, we can define a function that maps from a card to an integer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5ff533e",
   "metadata": {},
   "outputs": [],
   "source": [
    "suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)\n",
    "\n",
    "def spades_high_ordering(card):\n",
    "    rank_value = FrenchDeck.ranks.index(card.rank)\n",
    "    return rank_value * len(suit_values) + suit_values[card.suit]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aafe58e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "spades_high_ordering(Card('2', 'clubs'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f4b2395",
   "metadata": {},
   "outputs": [],
   "source": [
    "spades_high_ordering(Card('A', 'spades'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85198ed8",
   "metadata": {},
   "source": [
    "And then pass this funcition as a key to `sorted`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d7f7a8cf",
   "metadata": {},
   "outputs": [],
   "source": [
    "for card in sorted(deck, key=spades_high_ordering):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52ed3059",
   "metadata": {},
   "source": [
    "**Exercise**  Define a new ordering that sorts the cards by suit first and then by rank, so all clubs come first, followed by all diamonds, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3a7e102",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "efe14ef8",
   "metadata": {},
   "source": [
    "**Exercise**  Write a method called `setcard` that takes a deck, an index, and a card, and assigns the card to the deck at the given position.  Then monkey-patch `FrenchDeck` to provide `__setitem__` as a method.  Test it by assigning a new card like this:\n",
    "\n",
    "    deck[0] = Card('A', 'spades')\n",
    "\n",
    "Then shuffle the deck using `random.shuffle`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffa1f986",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b23d6075",
   "metadata": {},
   "source": [
    "We should have two Aces of spades now, which we can confirm by checking the number of unique cards:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "092a7ed7",
   "metadata": {},
   "outputs": [],
   "source": [
    "len(set(deck))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c919340b",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}


================================================
FILE: 01-data-model/frenchdeck.py
================================================
import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, position):
        return self._cards[position]


================================================
FILE: 01-data-model/frenchdeck_soln.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Card and Deck objects\n",
    "\n",
    "This notebook contains example code from [*Fluent Python*](http://shop.oreilly.com/product/0636920032519.do), by Luciano Ramalho.\n",
    "\n",
    "Code by Luciano Ramalho, modified by Allen Downey.\n",
    "\n",
    "MIT License: https://opensource.org/licenses/MIT"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This example demonstrates the Python data model using a simple implementation of playing cards and decks.\n",
    "\n",
    "`Card` is a namedtuple that represents a playing card."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import collections\n",
    "\n",
    "Card = collections.namedtuple('Card', ['rank', 'suit'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`FrenchDeck` is a class that represents a deck of cards."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "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\n",
    "                                        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": "markdown",
   "metadata": {},
   "source": [
    "You can instantiate a `Card` object as if `Card` were a class.\n",
    "\n",
    "BTW: [beer card](https://en.wikipedia.org/wiki/Beer_card)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "beer_card = Card('7', 'diamonds')\n",
    "beer_card"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can access the fields of a card by name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "beer_card.rank, beer_card.suit"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or by index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "beer_card[0], beer_card[1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A drawback of using namedtuples is that you can't define methods for them in the usual way.\n",
    "\n",
    "But you can [monkey-patch](https://en.wikipedia.org/wiki/Monkey_patch) them by defining a function and then making it an attribute of `Card`.  For example, here's a function that generates a string representation of a card:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def card_to_str(card):\n",
    "    return '%s of %s' % card\n",
    "\n",
    "card_to_str(beer_card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here's how we can make that function behave like a method.  When we pass a card to `print`, Python invokes the special method `__str__`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Card.__str__ = card_to_str\n",
    "print(beer_card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's instantiate a `FrenchDeck`.\n",
    "\n",
    "When we call `len`, Python invokes the `__len__` method on the deck. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "deck = FrenchDeck()\n",
    "len(deck)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When we use the bracket operator, Python invokes the `__getitem__` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "deck[3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And that means that the slice operator works, too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "deck[:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aside: In this context, we don't get the string generated by `__str__`; we get the one generated by `__repr__` (read about that [here](https://docs.python.org/3/reference/datamodel.html#basic-customization)) \n",
    "\n",
    "Because `FrenchDeck` provides `__len__` and `__getitem__`, it is considered a sequence, which means that the `in` operator works:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Card('Q', 'hearts') in deck"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise** Make up a card that doesn't exist and confirm that `in` yields `False`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "Card('Z', 'clubs') in deck"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And the for loop works, too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for card in deck:\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Other methods that work with sequences, like `random.choice`, will work with decks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from random import choice\n",
    "choice(deck)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sadly, `shuffle` doesn't work because we haven't provided `__setitem__`, so a deck is an immutable sequence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from random import shuffle\n",
    "\n",
    "# This should raise a TypeError\n",
    "shuffle(deck)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use `sorted` to iterate through the cards in the order determined by tuple comparison:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for card in sorted(deck):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want an ordering that makes more sense for cards, we can define a function that maps from a card to an integer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)\n",
    "\n",
    "def spades_high_ordering(card):\n",
    "    rank_value = FrenchDeck.ranks.index(card.rank)\n",
    "    return rank_value * len(suit_values) + suit_values[card.suit]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "spades_high_ordering(Card('2', 'clubs'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "spades_high_ordering(Card('A', 'spades'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And then pass this funcition as a key to `sorted`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for card in sorted(deck, key=spades_high_ordering):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise**  Define a new ordering that sorts the cards by suit first and then by rank, so all clubs come first, followed by all diamonds, etc."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "def spades_high_ordering_suit_first(card):\n",
    "    rank_value = FrenchDeck.ranks.index(card.rank)\n",
    "    return suit_values[card.suit] * len(FrenchDeck.ranks) + rank_value\n",
    "\n",
    "for card in sorted(deck, key=spades_high_ordering_suit_first):\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise**  Write a method called `setcard` that takes a deck, an index, and a card, and assigns the card to the deck at the given position.  Then monkey-patch `FrenchDeck` to provide `__setitem__` as a method.  Test it by assigning a new card like this:\n",
    "\n",
    "    deck[0] = Card('A', 'spades')\n",
    "\n",
    "Then shuffle the deck using `random.shuffle`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "def setcard(deck, position, card):\n",
    "    deck._cards[position] = card\n",
    "    \n",
    "FrenchDeck.__setitem__ = setcard\n",
    "\n",
    "deck[0] = Card('A', 'spades')\n",
    "\n",
    "from random import shuffle\n",
    "\n",
    "shuffle(deck)\n",
    "for card in deck:\n",
    "    print(card)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We should have two Aces of spades now, which we can confirm by checking the number of unique cards:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "len(set(deck))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}


================================================
FILE: 01-data-model/vector2d.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e3a55787",
   "metadata": {},
   "source": [
    "### 2-D Vectors\n",
    "\n",
    "This notebook contains example code from [*Fluent Python*](http://shop.oreilly.com/product/0636920032519.do), by Luciano Ramalho.\n",
    "\n",
    "Code by Luciano Ramalho, modified by Allen Downey.\n",
    "\n",
    "MIT License: https://opensource.org/licenses/MIT"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "341f33dd",
   "metadata": {},
   "source": [
    "This example demonstrates how a user-defined type can emulate a numeric type by providing special methods.\n",
    "\n",
    "`Vector` represents a 2-D Euclidean vector:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfc4c2fc",
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from math import hypot\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 'Vector(%r, %r)' % (self.x, self.y)\n",
    "\n",
    "    def __abs__(self):\n",
    "        return 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": "markdown",
   "id": "1a9472ca",
   "metadata": {},
   "source": [
    "Because `Vector` provides `__add__`, we can use the `+` operator to add Vectors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea8da064",
   "metadata": {},
   "outputs": [],
   "source": [
    "v1 = Vector(2, 4)\n",
    "v2 = Vector(2, 1)\n",
    "v1 + v2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc4b62ce",
   "metadata": {},
   "source": [
    "And because it provides `__abs__`, we can use the built-in method `abs`.  For Euclidean vectors, the \"absolute value\" is the magnitude; for 2-D vectors, the magnitude is the hypoteneuse of the two components:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51fcb372",
   "metadata": {},
   "outputs": [],
   "source": [
    "v = Vector(3, 4)\n",
    "abs(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c821fae",
   "metadata": {},
   "source": [
    "`Vector` provides `__mul__`, so we can use the `*` operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32a96cc4",
   "metadata": {},
   "outputs": [],
   "source": [
    "v * 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8793137c",
   "metadata": {},
   "source": [
    "But `__mul__` only supports scalar multiplication.\n",
    "\n",
    "**Exercise** What happens if you try to multiply two vectors?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2bdd4c4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fce039e2",
   "metadata": {},
   "source": [
    "`Vector` defines `__repr__`, which returns a string representation of the object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3f7cf9b",
   "metadata": {},
   "outputs": [],
   "source": [
    "repr(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5bf832cf",
   "metadata": {},
   "source": [
    "Because `Vector` does not provide `__str__`, Python uses `__repr__`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0841b67b",
   "metadata": {},
   "outputs": [],
   "source": [
    "str(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f718dff",
   "metadata": {},
   "source": [
    "So what's the difference?  `str` is meant to return a human-readable representation of the object.  `repr` should return a string that can be evaluated to re-create the object.\n",
    "\n",
    "If the same representation can perform both roles, you can just define `__repr__`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22a74b4d",
   "metadata": {},
   "source": [
    "`Vector` implements `__bool__`, so it can be used in a context where it has to be converted to `boolean`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "509f464a",
   "metadata": {},
   "outputs": [],
   "source": [
    "if v:\n",
    "    print(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f0edc15",
   "metadata": {},
   "source": [
    "If the magnitude is 0, the Vector is considered `False`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94bfe837",
   "metadata": {},
   "outputs": [],
   "source": [
    "if Vector(0, 0):\n",
    "    print(\"Won't happen.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9246a3c5",
   "metadata": {},
   "source": [
    "**Exercise** Create a class called `SubVector` that extends `Vector` and provides `__sub__`.  Test that you can use the `-` operator with `SubVector`.\n",
    "\n",
    "What happens if you subtract a `Vector` from a `SubVector`?  How about the other way around?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7cb4b655",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dddf76a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "84eac127",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0c0436b4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution goes here"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "507c04a3",
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}


================================================
FILE: 01-data-model/vector2d.py
================================================
from math import hypot

class Vector:

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __repr__(self):
        return 'Vector(%r, %r)' % (self.x, self.y)

    def __abs__(self):
        return hypot(self.x, self.y)

    def __bool__(self):
        return bool(abs(self))

    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Vector(x, y)

    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)


================================================
FILE: 01-data-model/vector2d_soln.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f79d3daf",
   "metadata": {},
   "source": [
    "### 2-D Vectors\n",
    "\n",
    "This notebook contains example code from [*Fluent Python*](http://shop.oreilly.com/product/0636920032519.do), by Luciano Ramalho.\n",
    "\n",
    "Code by Luciano Ramalho, modified by Allen Downey.\n",
    "\n",
    "MIT License: https://opensource.org/licenses/MIT"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1642a1df",
   "metadata": {},
   "source": [
    "This example demonstrates how a user-defined type can emulate a numeric type by providing special methods.\n",
    "\n",
    "`Vector` represents a 2-D Euclidean vector:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9cfa7e90",
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from math import hypot\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 'Vector(%r, %r)' % (self.x, self.y)\n",
    "\n",
    "    def __abs__(self):\n",
    "        return 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": "markdown",
   "id": "0f3c393b",
   "metadata": {},
   "source": [
    "Because `Vector` provides `__add__`, we can use the `+` operator to add Vectors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba142e1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "v1 = Vector(2, 4)\n",
    "v2 = Vector(2, 1)\n",
    "v1 + v2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39a29783",
   "metadata": {},
   "source": [
    "And because it provides `__abs__`, we can use the built-in method `abs`.  For Euclidean vectors, the \"absolute value\" is the magnitude; for 2-D vectors, the magnitude is the hypoteneuse of the two components:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8d3024d9",
   "metadata": {},
   "outputs": [],
   "source": [
    "v = Vector(3, 4)\n",
    "abs(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdb127ae",
   "metadata": {},
   "source": [
    "`Vector` provides `__mul__`, so we can use the `*` operator."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ba34b89",
   "metadata": {},
   "outputs": [],
   "source": [
    "v * 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d092330",
   "metadata": {},
   "source": [
    "But `__mul__` only supports scalar multiplication.\n",
    "\n",
    "**Exercise** What happens if you try to multiply two vectors?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0eb9758a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "v * v"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23d38ade",
   "metadata": {},
   "source": [
    "`Vector` defines `__repr__`, which returns a string representation of the object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "787f4113",
   "metadata": {},
   "outputs": [],
   "source": [
    "repr(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f487103",
   "metadata": {},
   "source": [
    "Because `Vector` does not provide `__str__`, Python uses `__repr__`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ad9bcc0",
   "metadata": {},
   "outputs": [],
   "source": [
    "str(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6edd234e",
   "metadata": {},
   "source": [
    "So what's the difference?  `str` is meant to return a human-readable representation of the object.  `repr` should return a string that can be evaluated to re-create the object.\n",
    "\n",
    "If the same representation can perform both roles, you can just define `__repr__`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2ed92ee8",
   "metadata": {},
   "source": [
    "`Vector` implements `__bool__`, so it can be used in a context where it has to be converted to `boolean`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bf4c1c65",
   "metadata": {},
   "outputs": [],
   "source": [
    "if v:\n",
    "    print(v)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a388a797",
   "metadata": {},
   "source": [
    "If the magnitude is 0, the Vector is considered `False`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2accdd9",
   "metadata": {},
   "outputs": [],
   "source": [
    "if Vector(0, 0):\n",
    "    print(\"Won't happen.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "266bfddc",
   "metadata": {},
   "source": [
    "**Exercise** Create a class called `SubVector` that extends `Vector` and provides `__sub__`.  Test that you can use the `-` operator with `SubVector`.\n",
    "\n",
    "What happens if you subtract a `Vector` from a `SubVector`?  How about the other way around?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fce8d80d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "class SubVector(Vector):\n",
    "\n",
    "    def __sub__(self, other):\n",
    "        x = self.x - other.x\n",
    "        y = self.y - other.y\n",
    "        return SubVector(x, y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6dfac21",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "v3 = SubVector(5, 6)\n",
    "v4 = SubVector(7, 8)\n",
    "\n",
    "v4 - v3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b55ade33",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "v4 - v2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e6c438ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Solution\n",
    "\n",
    "v2 - v4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f9837255",
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}


================================================
FILE: 04-text-byte/README.rst
================================================
Sample code for Chapter 4 - "Text and bytes"

From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
http://shop.oreilly.com/product/0636920032519.do


================================================
FILE: 04-text-byte/asciize.ipynb
================================================
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Extreme “Normalization”: Taking Out Diacritics\n",
    "\n",
    "Here are four functions that remove diacritical marks -- such as accents and cedillas -- and convert Unicode to more restricted character sets, including ASCII, with some data loss that may or may not be acceptable depending on the use case. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import unicodedata\n",
    "\n",
    "def shave_marks(txt):\n",
    "    \"\"\"Remove all diacritic marks\"\"\"\n",
    "    norm_txt = unicodedata.normalize('NFD', txt)  # <1>\n",
    "    shaved = ''.join(c for c in norm_txt\n",
    "                     if not unicodedata.combining(c))  # <2>\n",
    "    return unicodedata.normalize('NFC', shaved)  # <3>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'“Herr Voß: • ½ cup of Œtker™ caffe latte • bowl of acai.”'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "order = '“Herr Voß: • ½ cup of Œtker™ caffè latte • bowl of açaí.”'\n",
    "shave_marks(order)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Ζεφυρος, Zefiro'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "greek = 'Ζέφυρος, Zéfiro'\n",
    "shave_marks(greek)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import string\n",
    "\n",
    "def shave_marks_latin(txt):\n",
    "    \"\"\"Remove all diacritic marks from Latin base characters\"\"\"\n",
    "    norm_txt = unicodedata.normalize('NFD', txt)  # <1>\n",
    "    latin_base = False\n",
    "    keepers = []\n",
    "    for c in norm_txt:\n",
    "        if unicodedata.combining(c) and latin_base:   # <2>\n",
    "            continue  # ignore diacritic on Latin base char\n",
    "        keepers.append(c)                             # <3>\n",
    "        # if it isn't combining char, it's a new base char\n",
    "        if not unicodedata.combining(c):              # <4>\n",
    "            latin_base = c in string.ascii_letters\n",
    "    shaved = ''.join(keepers)\n",
    "    return unicodedata.normalize('NFC', shaved)   # <5>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'“Herr Voß: • ½ cup of Œtker™ caffe latte • bowl of acai.”'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shave_marks_latin(order)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shave_marks(order) == shave_marks_latin(order)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Ζέφυρος, Zefiro'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shave_marks_latin(greek)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "shave_marks(greek) == shave_marks_latin(greek)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "single_map = str.maketrans(\"\"\"‚ƒ„†ˆ‹‘’“”•–—˜›\"\"\",  # <1>\n",
    "                           \"\"\"'f\"*^<''\"\"---~>\"\"\")\n",
    "\n",
    "multi_map = str.maketrans({  # <2>\n",
    "    '€': '<euro>',\n",
    "    '…': '...',\n",
    "    'Œ': 'OE',\n",
    "    '™': '(TM)',\n",
    "    'œ': 'oe',\n",
    "    '‰': '<per mille>',\n",
    "    '‡': '**',\n",
    "})\n",
    "\n",
    "multi_map.update(single_map)  # <3>\n",
    "\n",
    "\n",
    "def dewinize(txt):\n",
    "    \"\"\"Replace Win1252 symbols with ASCII chars or sequences\"\"\"\n",
    "    return txt.translate(multi_map)  # <4>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\"Herr Voß: - ½ cup of OEtker(TM) caffè latte - bowl of açaí.\"'"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dewinize(order)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Ζέφυρος, Zéfiro'"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dewinize(greek)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def asciize(txt):\n",
    "    no_marks = shave_marks_latin(dewinize(txt))     # <5>\n",
    "    no_marks = no_marks.replace('ß', 'ss')          # <6>\n",
    "    return unicodedata.normalize('NFKC', no_marks)  # <7>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'\"Herr Voss: - 1⁄2 cup of OEtker(TM) caffe latte - bowl of acai.\"'"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "asciize(order)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Ζέφυρος, Zefiro'"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "asciize(greek)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}


================================================
FILE: 04-text-byte/asciize.py
================================================

"""
Radical folding and text sanitizing.

Handling a string with `cp1252` symbols:

    >>> order = '“Herr Voß: • ½ cup of Œtker™ caffè latte • bowl of açaí.”'
    >>> shave_marks(order)
    '“Herr Voß: • ½ cup of Œtker™ caffe latte • bowl of acai.”'
    >>> shave_marks_latin(order)
    '“Herr Voß: • ½ cup of Œtker™ caffe latte • bowl of acai.”'
    >>> dewinize(order)
    '"Herr Voß: - ½ cup of OEtker(TM) caffè latte - bowl of açaí."'
    >>> asciize(order)
    '"Herr Voss: - 1⁄2 cup of OEtker(TM) caffe latte - bowl of acai."'

Handling a string with Greek and Latin accented characters:

    >>> greek = 'Ζέφυρος, Zéfiro'
    >>> shave_marks(greek)
    'Ζεφυρος, Zefiro'
    >>> shave_marks_latin(greek)
    'Ζέφυρος, Zefiro'
    >>> dewinize(greek)
    'Ζέφυρος, Zéfiro'
    >>> asciize(greek)
    'Ζέφυρος, Zefiro'

"""

# BEGIN SHAVE_MARKS
import unicodedata
import string


def shave_marks(txt):
    """Remove all diacritic marks"""
    norm_txt = unicodedata.normalize('NFD', txt)  # <1>
    shaved = ''.join(c for c in norm_txt
                     if not unicodedata.combining(c))  # <2>
    return unicodedata.normalize('NFC', shaved)  # <3>
# END SHAVE_MARKS

# BEGIN SHAVE_MARKS_LATIN
def shave_marks_latin(txt):
    """Remove all diacritic marks from Latin base characters"""
    norm_txt = unicodedata.normalize('NFD', txt)  # <1>
    latin_base = False
    keepers = []
    for c in norm_txt:
        if unicodedata.combining(c) and latin_base:   # <2>
            continue  # ignore diacritic on Latin base char
        keepers.append(c)                             # <3>
        # if it isn't combining char, it's a new base char
        if not unicodedata.combining(c):              # <4>
            latin_base = c in string.ascii_letters
    shaved = ''.join(keepers)
    return unicodedata.normalize('NFC', shaved)   # <5>
# END SHAVE_MARKS_LATIN

# BEGIN ASCIIZE
single_map = str.maketrans("""‚ƒ„†ˆ‹‘’“”•–—˜›""",  # <1>
                           """'f"*^<''""---~>""")

multi_map = str.maketrans({  # <2>
    '€': '<euro>',
    '…': '...',
    'Œ': 'OE',
    '™': '(TM)',
    'œ': 'oe',
    '‰': '<per mille>',
    '‡': '**',
})

multi_map.update(single_map)  # <3>


def dewinize(txt):
    """Replace Win1252 symbols with ASCII chars or sequences"""
    return txt.translate(multi_map)  # <4>


def asciize(txt):
    no_marks = shave_marks_latin(dewinize(txt))     # <5>
    no_marks = no_marks.replace('ß', 'ss')          # <6>
    return unicodedata.normalize('NFKC', no_marks)  # <7>
# END ASCIIZE


================================================
FILE: 04-text-byte/default_encodings.py
================================================
import sys, locale

expressions = """
        locale.getpreferredencoding()
        type(my_file)
        my_file.encoding
        sys.stdout.isatty()
        sys.stdout.encoding
        sys.stdin.isatty()
        sys.stdin.encoding
        sys.stderr.isatty()
        sys.stderr.encoding
        sys.getdefaultencoding()
        sys.getfilesystemencoding()
    """

my_file = open('dummy', 'w')

for expression in expressions.split():
    value = eval(expression)
    print(expression.rjust(30), '->', repr(value))


================================================
FILE: 04-text-byte/normeq.py
================================================
"""
Utility functions for normalized Unicode string comparison.

Using Normal Form C, case sensitive:

    >>> s1 = 'café'
    >>> s2 = 'cafe\u0301'
    >>> s1 == s2
    False
    >>> nfc_equal(s1, s2)
    True
    >>> nfc_equal('A', 'a')
    False

Using Normal Form C with case folding:

    >>> s3 = 'Straße'
    >>> s4 = 'strasse'
    >>> s3 == s4
    False
    >>> nfc_equal(s3, s4)
    False
    >>> fold_equal(s3, s4)
    True
    >>> fold_equal(s1, s2)
    True
    >>> fold_equal('A', 'a')
    True

"""

from unicodedata import normalize

def nfc_equal(str1, str2):
    return normalize('NFC', str1) == normalize('NFC', str2)

def fold_equal(str1, str2):
    return (normalize('NFC', str1).casefold() ==
            normalize('NFC', str2).casefold())


================================================
FILE: 04-text-byte/numerics_demo.py
================================================
# BEGIN NUMERICS_DEMO
import unicodedata
import re

re_digit = re.compile(r'\d')

sample = '1\xbc\xb2\u0969\u136b\u216b\u2466\u2480\u3285'

for char in sample:
    print('U+%04x' % ord(char),                       # <1>
          char.center(6),                             # <2>
          're_dig' if re_digit.match(char) else '-',  # <3>
          'isdig' if char.isdigit() else '-',         # <4>
          'isnum' if char.isnumeric() else '-',       # <5>
          format(unicodedata.numeric(char), '5.2f'),  # <6>
          unicodedata.name(char),                     # <7>
          sep='\t')
# END NUMERICS_DEMO


================================================
FILE: 04-text-byte/ola.py
================================================
# coding: cp1252

print('Ol, Mundo!')


================================================
FILE: 04-text-byte/ramanujan.py
================================================
# BEGIN RE_DEMO
import re

re_numbers_str = re.compile(r'\d+')     # <1>
re_words_str = re.compile(r'\w+')
re_numbers_bytes = re.compile(rb'\d+')  # <2>
re_words_bytes = re.compile(rb'\w+')

text_str = ("Ramanujan saw \u0be7\u0bed\u0be8\u0bef"  # <3>
            " as 1729 = 1³ + 12³ = 9³ + 10³.")        # <4>

text_bytes = text_str.encode('utf_8')  # <5>

print('Text', repr(text_str), sep='\n  ')
print('Numbers')
print('  str  :', re_numbers_str.findall(text_str))      # <6>
print('  bytes:', re_numbers_bytes.findall(text_bytes))  # <7>
print('Words')
print('  str  :', re_words_str.findall(text_str))        # <8>
print('  bytes:', re_words_bytes.findall(text_bytes))    # <9>
# END RE_DEMO


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Luciano Ramalho

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.md
================================================
Fluent Python: notebooks
========================

This repository contains example code for the book `Fluent Python`_ by Luciano Ramalho (O'Reilly, 2014), with Jupyter notebooks added by Allen Downey.

You can run the notebooks by cloning this repo and running your own Jupyter server.  Or you can run them on Binder by pressing the button below.

[![Binder](http://mybinder.org/badge.svg)](http://mybinder.org/repo/AllenDowney/fluent-python-notebooks)



**BEWARE**: This is a work in progress, like the book itself.

* Code here may change and disappear without warning. 

* If a piece of code is not yet in the ebook, it's likely to be broken.

* A major reorganization may happen when the last chapter is done. 

* No promises. No guarantees. Use at own risk.

[*Fluent Python*](http://shop.oreilly.com/product/0636920032519.do)
Download .txt
gitextract_97iked2r/

├── .gitignore
├── 01-data-model/
│   ├── README.rst
│   ├── frenchdeck.doctest
│   ├── frenchdeck.ipynb
│   ├── frenchdeck.py
│   ├── frenchdeck_soln.ipynb
│   ├── vector2d.ipynb
│   ├── vector2d.py
│   └── vector2d_soln.ipynb
├── 04-text-byte/
│   ├── README.rst
│   ├── asciize.ipynb
│   ├── asciize.py
│   ├── default_encodings.py
│   ├── normeq.py
│   ├── numerics_demo.py
│   ├── ola.py
│   └── ramanujan.py
├── LICENSE
└── README.md
Download .txt
SYMBOL INDEX (17 symbols across 4 files)

FILE: 01-data-model/frenchdeck.py
  class FrenchDeck (line 5) | class FrenchDeck:
    method __init__ (line 9) | def __init__(self):
    method __len__ (line 13) | def __len__(self):
    method __getitem__ (line 16) | def __getitem__(self, position):

FILE: 01-data-model/vector2d.py
  class Vector (line 3) | class Vector:
    method __init__ (line 5) | def __init__(self, x=0, y=0):
    method __repr__ (line 9) | def __repr__(self):
    method __abs__ (line 12) | def __abs__(self):
    method __bool__ (line 15) | def __bool__(self):
    method __add__ (line 18) | def __add__(self, other):
    method __mul__ (line 23) | def __mul__(self, scalar):

FILE: 04-text-byte/asciize.py
  function shave_marks (line 36) | def shave_marks(txt):
  function shave_marks_latin (line 45) | def shave_marks_latin(txt):
  function dewinize (line 78) | def dewinize(txt):
  function asciize (line 83) | def asciize(txt):

FILE: 04-text-byte/normeq.py
  function nfc_equal (line 34) | def nfc_equal(str1, str2):
  function fold_equal (line 37) | def fold_equal(str1, str2):
Condensed preview — 19 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (66K chars).
[
  {
    "path": ".gitignore",
    "chars": 777,
    "preview": "\n*.sublime-project\n*.sublime-workspace\n.ipynb_checkpoints/\n.py311/\n\n# Byte-compiled / optimized / DLL files\n__pycache__/"
  },
  {
    "path": "01-data-model/README.rst",
    "chars": 168,
    "preview": "Sample code for Chapter 1 - \"The Python Data Model\"\n\nFrom the book \"Fluent Python\" by Luciano Ramalho (O'Reilly, 2015)\nh"
  },
  {
    "path": "01-data-model/frenchdeck.doctest",
    "chars": 1582,
    "preview": ">>> from frenchdeck import FrenchDeck, Card\n>>> beer_card = Card('7', 'diamonds')\n>>> beer_card\nCard(rank='7', suit='dia"
  },
  {
    "path": "01-data-model/frenchdeck.ipynb",
    "chars": 15744,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"id\": \"8683f614\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Card and D"
  },
  {
    "path": "01-data-model/frenchdeck.py",
    "chars": 487,
    "preview": "import collections\n\nCard = collections.namedtuple('Card', ['rank', 'suit'])\n\nclass FrenchDeck:\n    ranks = [str(n) for n"
  },
  {
    "path": "01-data-model/frenchdeck_soln.ipynb",
    "chars": 10669,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Card and Deck objects\\n\",\n    \""
  },
  {
    "path": "01-data-model/vector2d.ipynb",
    "chars": 6596,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"id\": \"e3a55787\",\n   \"metadata\": {},\n   \"source\": [\n    \"### 2-D Vector"
  },
  {
    "path": "01-data-model/vector2d.py",
    "chars": 509,
    "preview": "from math import hypot\n\nclass Vector:\n\n    def __init__(self, x=0, y=0):\n        self.x = x\n        self.y = y\n\n    def "
  },
  {
    "path": "01-data-model/vector2d_soln.ipynb",
    "chars": 6931,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"id\": \"f79d3daf\",\n   \"metadata\": {},\n   \"source\": [\n    \"### 2-D Vector"
  },
  {
    "path": "04-text-byte/README.rst",
    "chars": 161,
    "preview": "Sample code for Chapter 4 - \"Text and bytes\"\n\nFrom the book \"Fluent Python\" by Luciano Ramalho (O'Reilly, 2015)\nhttp://s"
  },
  {
    "path": "04-text-byte/asciize.ipynb",
    "chars": 6988,
    "preview": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Extreme “Normalization”: Taking O"
  },
  {
    "path": "04-text-byte/asciize.py",
    "chars": 2532,
    "preview": "\n\"\"\"\nRadical folding and text sanitizing.\n\nHandling a string with `cp1252` symbols:\n\n    >>> order = '“Herr Voß: • ½ cup"
  },
  {
    "path": "04-text-byte/default_encodings.py",
    "chars": 516,
    "preview": "import sys, locale\n\nexpressions = \"\"\"\n        locale.getpreferredencoding()\n        type(my_file)\n        my_file.encodi"
  },
  {
    "path": "04-text-byte/normeq.py",
    "chars": 761,
    "preview": "\"\"\"\nUtility functions for normalized Unicode string comparison.\n\nUsing Normal Form C, case sensitive:\n\n    >>> s1 = 'caf"
  },
  {
    "path": "04-text-byte/numerics_demo.py",
    "chars": 620,
    "preview": "# BEGIN NUMERICS_DEMO\nimport unicodedata\nimport re\n\nre_digit = re.compile(r'\\d')\n\nsample = '1\\xbc\\xb2\\u0969\\u136b\\u216b\\"
  },
  {
    "path": "04-text-byte/ola.py",
    "chars": 38,
    "preview": "# coding: cp1252\n\nprint('Ol, Mundo!')\n"
  },
  {
    "path": "04-text-byte/ramanujan.py",
    "chars": 698,
    "preview": "# BEGIN RE_DEMO\nimport re\n\nre_numbers_str = re.compile(r'\\d+')     # <1>\nre_words_str = re.compile(r'\\w+')\nre_numbers_by"
  },
  {
    "path": "LICENSE",
    "chars": 1083,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Luciano Ramalho\n\nPermission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "README.md",
    "chars": 834,
    "preview": "Fluent Python: notebooks\n========================\n\nThis repository contains example code for the book `Fluent Python`_ b"
  }
]

About this extraction

This page contains the full source code of the fluentpython/notebooks GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 19 files (56.3 KB), approximately 18.5k tokens, and a symbol index with 17 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!