diff --git a/.vscode/settings.js b/.vscode/settings.js new file mode 100644 index 00000000..d3d6c113 --- /dev/null +++ b/.vscode/settings.js @@ -0,0 +1,9 @@ +{ + "python-envs.pythonProjects": [ + { + "path": "", + "envManager": "ms-python.python:venv", + "packageManager": "ms-python.python:pip" + } + ] +} \ No newline at end of file diff --git a/chapters/chap01.ipynb b/chapters/chap01.ipynb index bd3875db..8af06309 100644 --- a/chapters/chap01.ipynb +++ b/chapters/chap01.ipynb @@ -46,7 +46,15 @@ "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Downloaded thinkpython.py\n" + ] + } + ], "source": [ "from os.path import basename, exists\n", "\n", @@ -103,7 +111,18 @@ "execution_count": 2, "id": "2568ec84", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "30 + 12" ] @@ -121,7 +140,18 @@ "execution_count": 3, "id": "c4e75456", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "43 - 1" ] @@ -139,7 +169,18 @@ "execution_count": 4, "id": "022a7b16", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "6 * 7" ] @@ -181,10 +222,21 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "4df5bcaa", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "84 // 2" ] @@ -199,10 +251,21 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "ef08d549", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "85 // 2" ] @@ -218,10 +281,21 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "id": "df933e80", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "49" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "7 ** 2" ] @@ -338,20 +412,42 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 8, "id": "1e3d5e01", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "round(42.4)" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 9, "id": "d1b220b9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "43" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "round(42.6)" ] @@ -418,12 +514,21 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 10, "id": "4674b7ca", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (3827346253.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[1;36m Cell \u001b[1;32mIn[10], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m abs 42\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ "%%expect SyntaxError\n", "\n", @@ -476,10 +581,21 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 11, "id": "bd8ae45f", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "'Hello'" ] @@ -494,10 +610,21 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 14, "id": "01d0055e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'world'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "\"world\"" ] @@ -512,10 +639,21 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 13, "id": "0295acab", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "\"it's a small \"" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "\"it's a small \"" ] @@ -530,10 +668,21 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 12, "id": "cf918917", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'Well, '" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "'Well, '" ] @@ -548,10 +697,21 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 16, "id": "aefe6af1", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "\"Well, it's a small world.\"" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "'Well, ' + \"it's a small \" + 'world.'" ] @@ -566,10 +726,21 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 15, "id": "42e9e4e2", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'Spam, Spam, Spam, Spam, '" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "'Spam, ' * 4" ] @@ -607,12 +778,21 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 17, "id": "e3f65f19", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (1580190030.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[1;36m Cell \u001b[1;32mIn[17], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m `Hello`\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ "%%expect SyntaxError\n", "\n", @@ -629,12 +809,21 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 18, "id": "a705b980", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid character '‘' (U+2018) (232449189.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[1;36m Cell \u001b[1;32mIn[18], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m ‘Hello’\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid character '‘' (U+2018)\n" + ] + } + ], "source": [ "%%expect SyntaxError\n", "\n", @@ -665,10 +854,21 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 19, "id": "3df8e2c5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "type(2)" ] @@ -683,10 +883,21 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 20, "id": "c4732c8d", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "type(42.0)" ] @@ -757,10 +968,21 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 21, "id": "f64e107c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'126'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "'126'" ] @@ -793,12 +1015,21 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 22, "id": "1cf21da4", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "TypeError", + "evalue": "unsupported operand type(s) for /: 'str' and 'int'", + "output_type": "error", + "traceback": [ + "\u001b[1;31mTypeError\u001b[0m\u001b[1;31m:\u001b[0m unsupported operand type(s) for /: 'str' and 'int'\n" + ] + } + ], "source": [ "%%expect TypeError\n", "\n", @@ -836,10 +1067,21 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 23, "id": "db30b719", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "12.6" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "float('12.6')" ] @@ -1023,12 +1265,20 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 24, "id": "06d3e72c", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exception reporting mode: Verbose\n" + ] + } + ], "source": [ "# This cell tells Jupyter to provide detailed debugging information\n", "# when a runtime error occurs. Run it before working on the exercises.\n", @@ -1285,7 +1535,7 @@ "metadata": { "celltoolbar": "Tags", "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "openai_venv (3.12.5)", "language": "python", "name": "python3" }, @@ -1299,7 +1549,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.12.5" } }, "nbformat": 4, diff --git a/chapters/chap02.ipynb b/chapters/chap02.ipynb index fb2369f7..c868001f 100644 --- a/chapters/chap02.ipynb +++ b/chapters/chap02.ipynb @@ -17,7 +17,15 @@ "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Downloaded diagram.py\n" + ] + } + ], "source": [ "from os.path import basename, exists\n", "\n", @@ -121,10 +129,19 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "id": "6bcc0a66", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'message' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31mNameError\u001b[0m\u001b[1;31m:\u001b[0m name 'message' is not defined\n" + ] + } + ], "source": [ "message" ] @@ -149,10 +166,19 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 3, "id": "6b2dafea", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'pi' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31mNameError\u001b[0m\u001b[1;31m:\u001b[0m name 'pi' is not defined\n" + ] + } + ], "source": [ "2 * pi" ] @@ -167,10 +193,19 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 4, "id": "72c45ac5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'pi' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31mNameError\u001b[0m\u001b[1;31m:\u001b[0m name 'pi' is not defined\n" + ] + } + ], "source": [ "round(pi)" ] @@ -198,7 +233,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "id": "2c25e84e", "metadata": { "tags": [] @@ -218,12 +253,23 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "id": "5b27a635", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAX4AAAB5CAYAAAA+qErwAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/GU6VOAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAa9UlEQVR4nO3deVRW1f7H8TcIclVkUFEUU8tUSDRMkeEBHnDCkJtagaal2HA1y8qMTO1iYamVWXidcwhJLXJoQMPlhIkKrsylGaWEAg2EIMpBNAHZvz9YnF/IaFcl7/m+1mKteM7e++yzz34+zz7nwY6FUkohhBDCMCwbuwNCCCFuLQl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGAl+IYQwGKuGFiwqKrqZ/RBCCPFfatmyZYPKyYpfCCEMRoJfCCEMRoJfCCEMRoJfCCEMRoJfCCEMRoJfCCEMRoJfCCEMRoJfCCEMRoJfCCEMRoL/NhYZGYm7uzt2dnYcP34cgHPnzmEymfSfPn364OjoSEFBQSP3Vgjxd9Hg/2WD+PsZMWIEL7zwAsHBwfprrVu35sCBA/rvixYtIjk5mVatWjVGF4UQf0OGX/Hb2dmxYMECAgMD6dWrFx999FGj9iczM5MNGzaQlpZGeXl5nWVNJhMuLi51llm3bh3jxo27kV0UQtzmDB/8ADY2NiQlJbF582ZefvllysrKGq0vTk5OtGjRgsTERGJjYxv0AVCb1NRULly4wNChQ29wL4UQtzO51QOEh4cD0L17d6ysrMjNza22kj58+DC//fbbLeuTi4sL58+fJzExkd27dxMWFoazs/N1tbFu3ToeeeQRrKzkNAsh/p8kAhUr/kqWlpaNuuK/US5evMjWrVtJSkpq7K4IIf5mJPgbqH///rdkP8XFxezcuZNff/0VR0dHAgICcHV1xdLy+u7KbdmyBXd3d7p3736TeiqEuF1J8P/N5OXlcenSJYYOHVpv4D///PPs2LGD3NxcRo4cia2tLceOHQMqbvNERETcol4LIW4nFkop1ZCC8gQuIYT4e5MncAkhhKiRBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBL8QQhiMBP81TCbTbfXQmeHDh+Pj44PJZCI4OFh/AtefZWVlERISQseOHTGZTDW2o5QiNDSUO+64Q3/t4sWLjBgxgi5dulR5vbJNBwcHTCaT/nP69Gl9e0xMDF5eXnh6ejJmzBguXLgAQE5ODiNGjOC+++7Dx8eHRx99lPz8fL3elStXmDZtGh4eHnh7e/Pkk0/q29zd3bnvvvv0/W3evLlB41BXvR07duDv74/JZMLLy4v169fr244cOcLgwYPx9fXFZDKxb98+fduYMWOqHLu9vT3bt28H4OzZs4wdOxYfHx/69evHkiVL9Hpffvml3k9PT0+io6Np4LOQhLhh5Alct7kLFy7g4OAAVITKvHnzOHjwYJUyBQUFnDx5ksLCQubMmcOBAweqtbN48WJOnjzJZ599xs8//wxUhHBKSgqOjo4MGzZMfx0qgt/Pz6/Ka5X27NnD9OnT2bNnDy1btuTtt9/m999/Z+HChZw9e5aMjAx8fHwAePXVV8nPz2f58uUAvPLKK5SVlfHOO+9gYWFBbm4u7dq1AyoCfMOGDfTu3fu6xqG2ekopOnfuzPbt23F3dycrK4t+/fpx+vRpbG1tcXNzY9myZQQFBZGens7w4cM5cuQIzZo1q9LOt99+y4MPPsipU6do2rQpTzzxBJ07dyYqKori4mKGDBnCokWL6Nu3L0VFRbRo0QJLS0tKSkoYMmQI06ZN45///GfNJ1iI6yBP4KqDnZ0d0dHR+Pn50adPHz755JMq2ypXp40hMzOTDRs2kJaWRnl5eb3lK8MOQNM0LCwsqpVp1aoVPj4+tGjRosY2fvjhBxISEpg6dWqV121sbDCbzdjb21/XMZw4cQIfHx99Eg4ZMkQf47Zt2+qhD9CvXz+ys7OBigfNx8XFERUVpR9HZejXpyHjUBMLCwsKCwuBisVNq1atsLGxoaCggPz8fIKCggDo1q0b9vb27Ny5s1obcXFxjBo1iqZNm+rHHxwcDECLFi3w9fXl448/BiremJXPUf7jjz8oKSlpcF+FuFEMGfxQ8YZPTk5my5YtREZGkpWV1dhdAsDJyYkWLVqQmJhIbGxsgz4A/vWvf+Hm5sYbb7zBypUrr2t/paWlTJkyhZiYGJo0aXJddYuLizGbzfj7+zN//nyuXr0KgIeHB0lJSeTm5qKUIj4+nqKiIgoKCqrUv3r1KitXriQkJASAM2fO4OjoyLvvvovZbCY4OJikpKQqdSZOnIi3tzfPPPNMlVtE9Y1DTfUsLCz48MMPGTt2LD179iQ4OJjly5fTtGlTWrdujbOzM1u2bAEqbvukp6frH1KVLl++zKZNmxg3bpz+moeHB/Hx8ZSXl5Ofn8/u3bur1EtNTcXb25uuXbsSEBDAsGHDrmvchfhvWTV2BxrL+PHjAbjzzjsxmUwcOHCAzp0711r+8OHD/Pbbb7eqe7i4uHD+/HkSExPZvXs3YWFhODs711i2MuTWr19PVFRUlXvY9Zk3bx4PPPAAPXr0uK4PP2dnZ06ePImTkxMFBQVMmDCB//znP7zwwgsEBAQwZcoUwsPDsbS01G9jWFn9/3RTSvHiiy/i4ODA5MmTASgrKyM7O5sePXrw+uuvc+zYMYYPH87hw4dp27YtX331FXfccQelpaXMmTOHiRMnVjnW2sahtnqVt5TWr1+PyWTiyJEjjB49mpSUFFq3bs3GjRuJiopi4cKFuLq64uPjU+3D8bPPPqNr16707NlTf23u3LnMmjULPz8/nJyc8Pf3r/Ih5eXlRUpKCvn5+Tz66KMcPHiw1u9ehLgZDLviv9b/wuX22LFj2b9/P+fOnWtwnQMHDrBixQrc3d0JDg5G0zTc3d2rraavZWNjg5OTE1BxK6kywCo99dRT7Nu3j7179+Ln54eLiwt2dnb69sjISH755Rc+/PBD/dbHHXfcgaWlJaNGjQLg3nvvpXPnznz//ff6dgBra2smT57MoUOHGjQOtdU7fvw4OTk5euj27duXDh066F8M9+rVi61bt5KcnMyqVavIycnBzc2tyr7i4uKqrPYBWrduzfLlyzl48CCff/45FhYWuLq6VutnmzZtGDJkCFu3bq1zrIW40Qy74v/oo4+YOXMmWVlZHDx4kPnz59dZvn///rekX8XFxezcuZNff/0VR0dHAgICcHV11cPxzy5cuMDly5dp3749AAkJCbRq1YpWrVo1eH87duzQ/7vyC9sTJ07UWy8vLw8HBwesra25cuUKX375ZZUvT3///XecnZ25dOkSb775Js8//7y+LTIyktOnT7Nx40b9vjhUBKbZbGbXrl0EBweTmZlJVlYWPXr0oLi4mNLSUv1e/qZNm/T91TUOddXr2LEjubm5nDx5kh49epCRkcGZM2fo1q1blWMA+PDDD2nRogVms1nvb0ZGBkePHtXv31c6d+4cdnZ2WFtbc+zYMRISEkhOTgbg1KlT3H333VhaWlJUVMSOHTt45JFH6h1vIW4kwwb/1atX8fPzo7i4mLfffrvO2zy3Ul5eHpcuXWLo0KG1Bn4lTdMYN24cf/zxB5aWlrRp04b4+HgsLCx49tlnCQkJISQkhEuXLnHfffdx5coVNE3D1dWV0aNH89prr9XbHx8fH/Lz8/V6/v7+fPDBBxw6dIg333yTJk2aUFZWRkBAAJGRkXq9ESNGUF5eTklJCaNHj2bixIkApKSksGLFCrp3786AAQMA6Ny5Mxs2bADg/fff59lnn2X27NlYWloSExNDhw4dOHPmDI899hhXr15FKUWXLl1YsWJFveNw9uzZWuu1bduWmJgYxo8fj6WlJeXl5SxYsEC/Qli7di3x8fEopejRowfr16+vcmX40Ucf8cADD1S5koGK7wNefvllrKyssLW1JTY2Vv8A2bx5M1u2bMHa2pqrV68yfPhw/bajELeKIf+c087Ojuzs7Cp/CSKEELc7+XNOIYQQNTLkrR5N0xq7C0II0WhkxS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwS+EEAYjwX8bc3d3r/K7UopevXrpDzf/Ky5evFjtiVKNYdu2bfTr1w+TyaQ/c/d2s2TJEnJzc/Xf586dy/Tp02ssu337dl555ZVb1bWbLiQkhISEhHrLrV+/nlOnTjWozbrGr6HS0tL0901OTg7BwcH6tmvnXGPMwWvHY//+/UyaNOmG70eC/39IUlIS9vb2nDhxgszMzMbuzn9l9erVTJ8+nQMHDtCzZ88G1ysrK7uJvbo+y5YtqxL8dQkJCan3uc//i64n+G+09u3bV3nm9LVzrjHm4K0aj5sS/HZ2drzzzjsEBQXh7u5OQkIC7777LmazGQ8PD/bv36+X3bVrF0OGDCEgIIDAwEC+/vprAH766ScGDx6Mr68v3t7eREdHA/DVV1/h4+ODyWTCy8uLbdu2AbB48WLMZjMmkwmz2Uxqaqq+j9TUVEwmE97e3kyePBlfX1+9D7m5uYwfP57AwMAq+2ksmZmZbNiwgbS0NMrLy+ss27p16yq/r1u3jvHjxxMWFkZcXJz++v79+/Hy8mLq1Kn4+vrSv39/vv32W337mjVr8PDwwM/PjyVLltS6v7lz5xIREUF4eDienp6EhoZSUFAAVDzD+NVXX8XLywsvLy9eeuklSkpKKC4uplOnTpSWlgIQGBjI448/DsDPP/9c5QHtlSIjIzl06BDR0dEMGjQIqJgn/v7++Pj4cP/99/Pjjz/qx9a/f3+eeeYZTCYTX375ZZW28vPzGT58ON7e3vj4+PD000/X2V+ASZMm8dxzz/HAAw/Qq1cvJk+ezDfffENISAi9e/dmxowZevu1zZ/58+eTk5NDREQEJpOJ48eP6+VrGr/169frD11vyPnq06cP/v7+vPXWW7VeoZWUlOjH6Ovry8iRI2/osYeEhPDSSy9hNpu59957mTlzJjU9ybWoqIgpU6YQGBiIj48Pzz33HCUlJcTGxnL06FFmzJiByWTSQ3jRokUEBgbi7+/PyJEjyc7Ortamt7d3lff42rVriYiIqHEc5s6di4eHBwEBAWzatEl/PSsrS3++8rVzrqY5eOTIEUJDQzGbzfj5+bF169Yq7URFReHv78+KFSvqzBV3d3feeOMNBg4cSK9evXj77bcBahyPpk2b3pwrcNVAmqY1+AdQ8+fPV5qmqS+++EK1aNFCLVu2TGmapmJjY1WfPn2Upmnq2LFjytPTU/3yyy9K0zR19OhR1a5dO5WXl6cmTZqkZs+erbeZmZmpNE1T7u7uaufOnUrTNHXhwgWVnZ2tNE1Tp0+f1svu2rVLdevWTWmapvLz85WLi4tKSEhQmqaphIQEBaht27YpTdPUgAED1Pbt25WmaaqgoEANHDhQxcbGXtfx3sifnJwcFRcXp1577TUVExOjUlJS1IULF+qtl5mZqRwcHFR2drY6ePCgcnFx0ett27ZNNWnSRO3evVtpmqbee+89NWDAAKVpmkpJSVFt27ZVp06dUpqmqWnTpimgxn288sorqlOnTurMmTNK0zT14IMP6udo4cKFys/PT+Xl5amCggI1ePBg9frrrytN05Svr6/66quvVFZWlurVq5e66667VGFhoVq8eLGKiIiocV9+fn5qw4YNStM0lZGRoRwdHdWhQ4eUpmnqgw8+UD169FCFhYVq27ZtysLCQj+H1/7MnTtXTZgwodo8qqu/Y8aMUZ6enurs2bMqPz9f3XnnnSo0NFSdO3dO5eTkKCcnJ5Wamlrv/OnUqZNKTk5u0PgtW7ZMDRs2rEHnq127dio9PV1pmqamT59e6/maMWOGCgkJUXl5eVXeIzfq2P38/JTZbFbnzp1Tv//+u+rTp49atWpVtfMXERGhli9frjRNU4WFhWrcuHFqzpw51cppmqZWrVqlxo8fr86fP680TVMrVqxQQ4YM0cfv6aefVpqmqUWLFqmHH35Yr+fu7q4SExOrjUF8fLxydXVVv/zyiyosLFTh4eGqU6dOStM09d133yl7e/sa59y1v2dnZ6vevXvr75MzZ86ojh07qh9//FF99913CtCPsSHzYuLEiXo7dnZ26scff6yxD9f701A37dGLDz30EAB9+vShuLhY/71v376cPn0aqFjFnT59mvvvv1+vZ2lpyc8//4zJZOLf//43xcXFmEwmgoKCADCbzUyfPp0RI0YwYMAAfcV47NgxFixYQEFBAVZWVqSnp3P58mUyMjKwsrIiICAAgICAAO68804AiouL2bdvH3l5efr+L168SHp6erXjOXz4ML/99tuNHqZaubi4cP78eRITE9m9ezdhYWE4OzvXWj4+Pp5Bgwbh4OCAg4MDbdu21a+mAO666y48PT0B6N+/P4sWLQJg3759DB48WG/7iSee4N133611P4MGDdKvNPr3709aWhpQcZtp7Nix2NjYABAREcEHH3zA1KlTCQwMZO/eveTn5zNgwABOnjzJ999/z969ewkNDa13LL755ht69uypX26PGjWKl156ST8fXbp0wc/Pr8a6np6eLF26lJkzZ2IymfTVW139BRg2bBj/+Mc/AOjZsycDBw7E2toaa2trXF1dycjIoFOnTg2eP/WN37XqOl+DBg2iXbt2er/feuutGttITEwkOjpaP8Y2bdrcsGN3c3MDYPTo0fq28PBwkpKSCA8Pr9KPhIQEDh8+rF9NXr58mSZNmtTY523btvHtt9/q79erV6/WWG7UqFG8+eabnD17loyMDCwsLPD19a1Wbt++fYwcOVJfNT/++OOkpKTU2GZdUlNTyczM1HOsUnp6Ol26dMHa2prRo0cDDcuVsLAwoOKqvUuXLmRlZdGhQ4fr7tdfddOCv3JSVZ7gyonUpEkT/R6YUoqgoCDWrFlTrf7d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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "from diagram import diagram, adjust\n", "\n", @@ -262,12 +308,21 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 7, "id": "ac2620ef", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (4061783710.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[1;36m Cell \u001b[1;32mIn[7], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m million! = 1000000\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ "%%expect SyntaxError\n", "\n", @@ -306,12 +361,21 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 8, "id": "b6938851", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (3285659805.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[1;36m Cell \u001b[1;32mIn[8], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m class = 'Self-Defence Against Fresh Fruit'\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ "%%expect SyntaxError\n", "\n", @@ -347,12 +411,23 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 9, "id": "4a8f4b3e", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "35" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "from keyword import kwlist\n", "\n", @@ -541,10 +616,19 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 10, "id": "805977c6", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'n' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31mNameError\u001b[0m\u001b[1;31m:\u001b[0m name 'n' is not defined\n" + ] + } + ], "source": [ "n + 1" ] @@ -559,10 +643,19 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 11, "id": "962e08ab", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'n' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31mNameError\u001b[0m\u001b[1;31m:\u001b[0m name 'n' is not defined\n" + ] + } + ], "source": [ "n + 2\n", "n + 3" @@ -740,12 +833,21 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 12, "id": "69295e52", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "TypeError", + "evalue": "float expected at most 1 argument, got 2", + "output_type": "error", + "traceback": [ + "\u001b[1;31mTypeError\u001b[0m\u001b[1;31m:\u001b[0m float expected at most 1 argument, got 2\n" + ] + } + ], "source": [ "%%expect TypeError\n", "\n", @@ -762,12 +864,21 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 13, "id": "edec7064", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "TypeError", + "evalue": "pow expected 2 arguments, got 1", + "output_type": "error", + "traceback": [ + "\u001b[1;31mTypeError\u001b[0m\u001b[1;31m:\u001b[0m pow expected 2 arguments, got 1\n" + ] + } + ], "source": [ "%%expect TypeError\n", "\n", @@ -820,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 15, "id": "607893a6", "metadata": {}, "outputs": [], @@ -840,7 +951,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 14, "id": "615a11e7", "metadata": {}, "outputs": [], @@ -926,12 +1037,21 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 16, "id": "86f07f6e", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (4061783710.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[1;36m Cell \u001b[1;32mIn[16], line 1\u001b[1;36m\u001b[0m\n\u001b[1;33m million! = 1000000\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ "%%expect SyntaxError\n", "\n", @@ -1052,12 +1172,20 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 17, "id": "c9e6cab4", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exception reporting mode: Verbose\n" + ] + } + ], "source": [ "# This cell tells Jupyter to provide detailed debugging information\n", "# when a runtime error occurs. Run it before working on the exercises.\n", @@ -1227,7 +1355,7 @@ "metadata": { "celltoolbar": "Tags", "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "openai_venv (3.12.5)", "language": "python", "name": "python3" }, @@ -1241,12 +1369,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" - }, - "vscode": { - "interpreter": { - "hash": "357b915890fbc73e00b3ee3cc7035b34e6189554c2854644fe780ff20c2fdfc0" - } + "version": "3.12.5" } }, "nbformat": 4, diff --git a/chapters/diagram.py b/chapters/diagram.py new file mode 100644 index 00000000..b33f97dc --- /dev/null +++ b/chapters/diagram.py @@ -0,0 +1,386 @@ +import matplotlib.pyplot as plt +import matplotlib.patches as patches + +from matplotlib.transforms import Bbox, TransformedBbox + +# TODO: Study this https://matplotlib.org/stable/tutorials/text/annotations.html#sphx-glr-tutorials-text-annotations-py + + +def override(d1, **d2): + """Add key-value pairs to d. + + d1: dictionary + d2: keyword args to add to d + + returns: new dict + """ + d = d1.copy() + d.update(d2) + return d + +def underride(d1, **d2): + """Add key-value pairs to d only if key is not in d. + + d1: dictionary + d2: keyword args to add to d + + returns: new dict + """ + d = d2.copy() + d.update(d1) + return d + +def diagram(width=5, height=1, **options): + fig, ax = plt.subplots(**options) + + # TODO: dpi in the notebook should be 100, in the book it should be 300 or 600 + # fig.set_dpi(100) + + # Set figure size + fig.set_size_inches(width, height) + + plt.rc('font', size=8) + + # Set axes position + ax.set_position([0, 0, 1, 1]) + + # Set x and y limits + ax.set_xlim(0, width) + ax.set_ylim(0, height) + + # Remove the spines, ticks, and labels + despine(ax) + return ax + +def despine(ax): + # Remove the spines + ax.spines['right'].set_visible(False) + ax.spines['top'].set_visible(False) + ax.spines['bottom'].set_visible(False) + ax.spines['left'].set_visible(False) + + # Remove the axis labels + ax.set_xticklabels([]) + ax.set_yticklabels([]) + + # Remove the tick marks + ax.tick_params(axis='both', which='both', length=0, width=0) + +def adjust(x, y, bbox): + """Adjust the coordinates of a point based on a bounding box. + + x: x coordinate + y: y coordinate + bbox: Bbox object + + returns: tuple of coordinates + """ + width = bbox.width + height = bbox.height + 0.2 + t = width, height, x - bbox.x0, y - bbox.y0 + 0.1 + return [round(x, 2) for x in t] + +def get_bbox(ax, handle): + bbox = handle.get_window_extent() + transformed = TransformedBbox(bbox, ax.transData.inverted()) + return transformed + +def draw_bbox(ax, bbox, **options): + options = underride(options, facecolor='gray', alpha=0.1, linewidth=0) + rect = patches.Rectangle((bbox.xmin, bbox.ymin), bbox.width, bbox.height, **options) + handle = ax.add_patch(rect) + bbox = get_bbox(ax, handle) + return bbox + +def draw_box_around(ax, bboxes, **options): + bbox = Bbox.union(bboxes) + return draw_bbox(ax, padded(bbox), **options) + +def padded(bbox, dx=0.1, dy=0.1): + """Add padding to a bounding box. + """ + [x0, y0], [x1, y1] = bbox.get_points() + return Bbox([[x0-dx, y0-dy], [x1+dx, y1+dy]]) + +def make_binding(name, value, **options): + """Make a binding between a name and a value. + + name: string + value: any type + + returns: Binding object + """ + if not isinstance(value, Frame): + value = Value(repr(value)) + + return Binding(Value(name), value, **options) + +def make_mapping(key, value, **options): + """Make a binding between a key and a value. + + key: any type + value: any type + + returns: Binding object + """ + return Binding(Value(repr(key)), Value(repr(value)), **options) + +def make_dict(d, name='dict', **options): + """Make a Frame that represents a dictionary. + + d: dictionary + name: string + options: passed to Frame + """ + mappings = [make_mapping(key, value) for key, value in d.items()] + return Frame(mappings, name=name, **options) + +def make_frame(d, name='frame', **options): + """Make a Frame that represents a stack frame. + + d: dictionary + name: string + options: passed to Frame + """ + bindings = [make_binding(key, value) for key, value in d.items()] + return Frame(bindings, name=name, **options) + +class Binding(object): + def __init__(self, name, value=None, **options): + """ Represents a binding between a name and a value. + + name: Value object + value: Value object + """ + self.name = name + self.value = value + self.options = options + + def draw(self, ax, x, y, **options): + options = override(self.options, **options) + dx = options.pop('dx', 0.4) + dy = options.pop('dy', 0) + draw_value = options.pop('draw_value', True) + + bbox1 = self.name.draw(ax, x, y, ha='right') + bboxes = [bbox1] + + arrow = Arrow(dx=dx, dy=dy, **options) + bbox2 = arrow.draw(ax, x, y) + + if draw_value: + bbox3 = self.value.draw(ax, x+dx, y+dy) + # only include the arrow if we drew the value + bboxes.extend([bbox2, bbox3]) + + bbox = Bbox.union(bboxes) + # draw_bbox(ax, self.bbox) + self.bbox = bbox + return bbox + + +class Element(object): + def __init__(self, index, value, **options): + """ Represents a an element of a list. + + index: integer + value: Value object + """ + self.index = index + self.value = value + self.options = options + + def draw(self, ax, x, y, dx=0.15, **options): + options = override(self.options, **options) + draw_value = options.pop('draw_value', True) + + bbox1 = self.index.draw(ax, x, y, ha='right', fontsize=6, color='gray') + bboxes = [bbox1] + + if draw_value: + bbox2 = self.value.draw(ax, x+dx, y) + bboxes.append(bbox2) + + bbox = Bbox.union(bboxes) + self.bbox = bbox + # draw_bbox(ax, self.bbox) + return bbox + + +class Value(object): + def __init__(self, value): + self.value = value + self.options = dict(ha='left', va='center') + self.bbox = None + + def draw(self, ax, x, y, **options): + options = override(self.options, **options) + + handle = ax.text(x, y, self.value, **options) + bbox = self.bbox = get_bbox(ax, handle) + # draw_bbox(ax, bbox) + self.bbox = bbox + return bbox + + +class Arrow(object): + def __init__(self, **options): + # Note for the future about dotted arrows + # self.arrowprops = dict(arrowstyle="->", ls=':') + arrowprops = dict(arrowstyle="->", color='gray') + options = underride(options, arrowprops=arrowprops) + self.options = options + + def draw(self, ax, x, y, **options): + options = override(self.options, **options) + dx = options.pop('dx', 0.5) + dy = options.pop('dy', 0) + shim = options.pop('shim', 0.02) + + handle = ax.annotate("", [x+dx, y+dy], [x+shim, y], **options) + bbox = get_bbox(ax, handle) + self.bbox = bbox + return bbox + + +class ReturnArrow(object): + def __init__(self, **options): + style = "Simple, tail_width=0.5, head_width=4, head_length=8" + options = underride(options, arrowstyle=style, color="gray") + self.options = options + + def draw(self, ax, x, y, **options): + options = override(self.options, **options) + value = options.pop('value', None) + dx = options.pop('dx', 0) + dy = options.pop('dy', 0.4) + shim = options.pop('shim', 0.02) + + x += shim + arrow = patches.FancyArrowPatch((x, y), (x+dx, y+dy), + connectionstyle="arc3,rad=.6", **options) + handle = ax.add_patch(arrow) + bbox = get_bbox(ax, handle) + + if value is not None: + handle = plt.text(x+0.15, y+dy/2, str(value), ha='left', va='center') + bbox2 = get_bbox(ax, handle) + bbox = Bbox.union([bbox, bbox2]) + + self.bbox = bbox + return bbox + + +class Frame(object): + def __init__(self, bindings, **options): + self.bindings = bindings + self.options = options + + def draw(self, ax, x, y, **options): + options = override(self.options, **options) + name = options.pop('name', '') + value = options.pop('value', None) + dx = options.pop('dx', 0) + dy = options.pop('dy', 0) + offsetx = options.pop('offsetx', 0) + offsety = options.pop('offsety', 0) + shim = options.pop('shim', 0) + loc = options.pop('loc', 'top') + box_around = options.pop('box_around', None) + + x += offsetx + y += offsety + save_y = y + + if len(self.bindings) == 0: + bbox = Bbox([[x, y], [x, y]]) + bboxes = [bbox] + else: + bboxes = [] + + # draw the bindings + for binding in self.bindings: + bbox = binding.draw(ax, x, y) + bboxes.append(bbox) + x += dx + y += dy + + if box_around: + bbox1 = draw_bbox(ax, box_around, **options) + else: + bbox1 = draw_box_around(ax, bboxes, **options) + bboxes.append(bbox1) + + if value is not None: + arrow = ReturnArrow(value=value) + x = bbox1.xmax + shim + bbox2 = arrow.draw(ax, x, save_y, value=value) + bboxes.append(bbox2) + + if name: + if loc == 'top': + x = bbox1.xmin + y = bbox1.ymax + 0.02 + handle = plt.text(x, y, name, ha='left', va='bottom') + elif loc == 'left': + x = bbox1.xmin - 0.1 + y = save_y + handle = plt.text(x, y, name, ha='right', va='center') + bbox3 = get_bbox(ax, handle) + bboxes.append(bbox3) + + bbox = Bbox.union(bboxes) + self.bbox = bbox + return bbox + + +class Stack(object): + def __init__(self, frames, **options): + self.frames = frames + self.options = options + + def draw(self, ax, x, y, **options): + options = override(self.options, **options) + dx = options.pop('dx', 0) + dy = options.pop('dy', -0.4) + + # draw the frames + bboxes = [] + for frame in self.frames: + bbox = frame.draw(ax, x, y) + bboxes.append(bbox) + x += dx + y += dy + + bbox = Bbox.union(bboxes) + self.bbox = bbox + return bbox + +def make_rebind(name, seq): + bindings = [] + for i, value in enumerate(seq): + dy = dy=-0.3*i + if i == len(seq)-1: + binding = make_binding(name, value, dy=dy) + else: + arrowprops = dict(arrowstyle="->", color='gray', ls=':') + binding = make_binding('', value, dy=dy, arrowprops=arrowprops) + bindings.append(binding) + + return bindings + +def make_element(index, value): + return Element(Value(index), Value(repr(value))) + +def make_list(seq, name='list', **options): + elements = [make_element(index, value) for index, value in enumerate(seq)] + return Frame(elements, name=name, **options) + +def draw_bindings(bindings, ax, x, y): + bboxes = [] + for binding in bindings: + bbox = binding.draw(ax, x, y) + bboxes.append(bbox) + + bbox = Bbox.union(bboxes) + return bbox \ No newline at end of file diff --git a/chapters/thinkpython.py b/chapters/thinkpython.py new file mode 100644 index 00000000..3f08885a --- /dev/null +++ b/chapters/thinkpython.py @@ -0,0 +1,93 @@ +import contextlib +import io +import re + + +def extract_function_name(text): + """Find a function definition and return its name. + + text: String + + returns: String or None + """ + pattern = r"def\s+(\w+)\s*\(" + match = re.search(pattern, text) + if match: + func_name = match.group(1) + return func_name + else: + return None + + +# the functions that define cell magic commands are only defined +# if we're running in Jupyter. + +try: + from IPython.core.magic import register_cell_magic + from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring + + @register_cell_magic + def add_method_to(args, cell): + + # get the name of the function defined in this cell + func_name = extract_function_name(cell) + if func_name is None: + return f"This cell doesn't define any new functions." + + # get the class we're adding it to + namespace = get_ipython().user_ns + class_name = args + cls = namespace.get(class_name, None) + if cls is None: + return f"Class '{class_name}' not found." + + # save the old version of the function if it was already defined + old_func = namespace.get(func_name, None) + if old_func is not None: + del namespace[func_name] + + # Execute the cell to define the function + get_ipython().run_cell(cell) + + # get the newly defined function + new_func = namespace.get(func_name, None) + if new_func is None: + return f"This cell didn't define {func_name}." + + # add the function to the class and remove it from the namespace + setattr(cls, func_name, new_func) + del namespace[func_name] + + # restore the old function to the namespace + if old_func is not None: + namespace[func_name] = old_func + + @register_cell_magic + def expect_error(line, cell): + try: + get_ipython().run_cell(cell) + except Exception as e: + get_ipython().run_cell("%tb") + + @magic_arguments() + @argument("exception", help="Type of exception to catch") + @register_cell_magic + def expect(line, cell): + args = parse_argstring(expect, line) + exception = eval(args.exception) + try: + get_ipython().run_cell(cell) + except exception as e: + get_ipython().run_cell("%tb") + + def traceback(mode): + """Set the traceback mode. + + mode: string + """ + with contextlib.redirect_stdout(io.StringIO()): + get_ipython().run_cell(f"%xmode {mode}") + + traceback("Minimal") +except (ImportError, NameError): + print("Warning: IPython is not available, cell magic not defined.")