diff --git a/01-data-model/data-model.ipynb b/01-data-model/data-model.ipynb index f525899..5b30397 100644 --- a/01-data-model/data-model.ipynb +++ b/01-data-model/data-model.ipynb @@ -482,7 +482,7 @@ " self.y = y\n", "\n", " def __repr__(self):\n", - " return 'Vector(%r, %r)' % (self.x, self.y)\n", + " return f'Vector({self.x!r}, {self.y!r})'\n", "\n", " def __abs__(self):\n", " return math.hypot(self.x, self.y)\n", diff --git a/02-array-seq/array-seq.ipynb b/02-array-seq/array-seq.ipynb index 032d811..afdbf6b 100644 --- a/02-array-seq/array-seq.ipynb +++ b/02-array-seq/array-seq.ipynb @@ -9,12 +9,14 @@ "**Sections with code snippets in this chapter:**\n", "\n", "* [List Comprehensions and Generator Expressions](#List-Comprehensions-and-Generator-Expressions)\n", + "* [Tuples Are Not Just Immutable Lists](#Tuples-Are-Not-Just-Immutable-Lists)\n", + "* [Unpacking sequences and iterables](#Unpacking-sequences-and-iterables)\n", + "* [Pattern Matching with Sequences](#Pattern-Matching-with-Sequences)\n", "* [Slicing](#Slicing)\n", - "* [Building Lists of Lists](#Building-Lists-of-Lists)\n", + "* [Using + and * with Sequences](#Using-+-and-*-with-Sequences)\n", "* [Augmented Assignment with Sequences](#Augmented-Assignment-with-Sequences)\n", "* [list.sort and the sorted Built-In Function](#list.sort-and-the-sorted-Built-In-Function)\n", - "* [Managing Ordered Sequences with bisect](#Managing-Ordered-Sequences-with-bisect)\n", - "* [Arrays](#Arrays)\n", + "* [When a List Is Not the Answer](#When-a-List-Is-Not-the-Answer)\n", "* [Memory Views](#Memory-Views)\n", "* [NumPy and SciPy](#NumPy-and-SciPy)\n", "* [Deques and Other Queues](#Deques-and-Other-Queues)\n", @@ -42,9 +44,7 @@ "outputs": [ { "data": { - "text/plain": [ - "[36, 162, 163, 165, 8364, 164]" - ] + "text/plain": "[36, 162, 163, 165, 8364, 164]" }, "execution_count": 1, "metadata": {}, @@ -65,7 +65,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-2. Build a list of Unicode codepoints from a string, take 2" + "#### Example 2-2. Build a list of Unicode codepoints from a string, using a listcomp" ] }, { @@ -75,9 +75,7 @@ "outputs": [ { "data": { - "text/plain": [ - "[36, 162, 163, 165, 8364, 164]" - ] + "text/plain": "[36, 162, 163, 165, 8364, 164]" }, "execution_count": 2, "metadata": {}, @@ -106,9 +104,7 @@ "outputs": [ { "data": { - "text/plain": [ - "'ABC'" - ] + "text/plain": "'ABC'" }, "execution_count": 3, "metadata": {}, @@ -128,9 +124,7 @@ "outputs": [ { "data": { - "text/plain": [ - "[65, 66, 67]" - ] + "text/plain": "[65, 66, 67]" }, "execution_count": 4, "metadata": {}, @@ -141,6 +135,30 @@ "codes" ] }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [ + { + "data": { + "text/plain": "67" + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "codes = [last := ord(c) for c in x]\n", + "last" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, { "cell_type": "markdown", "metadata": {}, @@ -150,214 +168,958 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[162, 163, 165, 8364, 164]" + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = '$¢£¥€¤'\n", + "beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]\n", + "beyond_ascii" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[162, 163, 165, 8364, 164]" + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))\n", + "beyond_ascii" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2-4. Cartesian product using a list comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[('black', 'S'),\n ('black', 'M'),\n ('black', 'L'),\n ('white', 'S'),\n ('white', 'M'),\n ('white', 'L')]" + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "colors = ['black', 'white']\n", + "sizes = ['S', 'M', 'L']\n", + "tshirts = [(color, size) for color in colors for size in sizes]\n", + "tshirts" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('black', 'S')\n", + "('black', 'M')\n", + "('black', 'L')\n", + "('white', 'S')\n", + "('white', 'M')\n", + "('white', 'L')\n" + ] + } + ], + "source": [ + "for color in colors:\n", + " for size in sizes:\n", + " print((color, size))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "[('black', 'S'),\n ('black', 'M'),\n ('black', 'L'),\n ('white', 'S'),\n ('white', 'M'),\n ('white', 'L')]" + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "shirts = [(color, size) for size in sizes\n", + " for color in colors]\n", + "tshirts" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2-5. Initializing a tuple and an array from a generator expression" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "(36, 162, 163, 165, 8364, 164)" + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "symbols = '$¢£¥€¤'\n", + "tuple(ord(symbol) for symbol in symbols)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": "array('I', [36, 162, 163, 165, 8364, 164])" + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import array\n", + "\n", + "array.array('I', (ord(symbol) for symbol in symbols))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Example 2-6. Cartesian product in a generator expression" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "black S\n", + "black M\n", + "black L\n", + "white S\n", + "white M\n", + "white L\n" + ] + } + ], + "source": [ + "colors = ['black', 'white']\n", + "sizes = ['S', 'M', 'L']\n", + "\n", + "for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):\n", + " print(tshirt)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Tuples Are Not Just Immutable Lists" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + }, + "execution_count": 73 + }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-7. Tuples used as records" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "BRA/CE342567\n", + "ESP/XDA205856\n", + "USA/31195855\n" + ] + } + ], + "source": [ + "lax_coordinates = (33.9425, -118.408056)\n", + "city, year, pop, chg, area = ('Tokyo', 2003, 32_450, 0.66, 8014)\n", + "traveler_ids = [('USA', '31195855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]\n", + "\n", + "for passport in sorted(traveler_ids):\n", + " print('%s/%s' % passport)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 15, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "USA\n", + "BRA\n", + "ESP\n" + ] + } + ], + "source": [ + "for country, _ in traveler_ids:\n", + " print(country)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Tuples as Immutable Lists" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 16, + "outputs": [ + { + "data": { + "text/plain": "True" + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = (10, 'alpha', [1, 2])\n", + "b = (10, 'alpha', [1, 2])\n", + "a == b" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 17, + "outputs": [ + { + "data": { + "text/plain": "False" + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b[-1].append(99)\n", + "a == b" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 18, + "outputs": [ + { + "data": { + "text/plain": "(10, 'alpha', [1, 2, 99])" + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 19, + "outputs": [ + { + "data": { + "text/plain": "True" + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def fixed(o):\n", + " try:\n", + " hash(o)\n", + " except TypeError:\n", + " return False\n", + " return True\n", + "\n", + "\n", + "tf = (10, 'alpha', (1, 2)) # Contains no mutable items\n", + "tm = (10, 'alpha', [1, 2]) # Contains a mutable item (list)\n", + "fixed(tf)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 20, + "outputs": [ + { + "data": { + "text/plain": "False" + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fixed(tm)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Unpacking sequences and iterables" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 21, + "outputs": [ + { + "data": { + "text/plain": "33.9425" + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lax_coordinates = (33.9425, -118.408056)\n", + "latitude, longitude = lax_coordinates # unpacking\n", + "latitude" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 22, + "outputs": [ + { + "data": { + "text/plain": "-118.408056" + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "longitude" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 23, + "outputs": [ + { + "data": { + "text/plain": "(2, 4)" + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "divmod(20, 8)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 24, + "outputs": [ + { + "data": { + "text/plain": "(2, 4)" + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "t = (20, 8)\n", + "divmod(*t)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 25, + "outputs": [ + { + "data": { + "text/plain": "(2, 4)" + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "quotient, remainder = divmod(*t)\n", + "quotient, remainder" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 26, + "outputs": [ + { + "data": { + "text/plain": "'id_rsa.pub'" + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "\n", + "_, filename = os.path.split('/home/luciano/.ssh/id_rsa.pub')\n", + "filename" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Using * to grab excess items" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 27, + "outputs": [ + { + "data": { + "text/plain": "(0, 1, [2, 3, 4])" + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b, *rest = range(5)\n", + "a, b, rest" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 28, + "outputs": [ + { + "data": { + "text/plain": "(0, 1, [2])" + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b, *rest = range(3)\n", + "a, b, rest" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 29, "outputs": [ { "data": { - "text/plain": [ - "[162, 163, 165, 8364, 164]" - ] + "text/plain": "(0, 1, [])" }, - "execution_count": 5, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "symbols = '$¢£¥€¤'\n", - "beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]\n", - "beyond_ascii" - ] + "a, b, *rest = range(2)\n", + "a, b, rest" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, + "execution_count": 30, "outputs": [ { "data": { - "text/plain": [ - "[162, 163, 165, 8364, 164]" - ] + "text/plain": "(0, [1, 2], 3, 4)" }, - "execution_count": 6, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))\n", - "beyond_ascii" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Example 2-4. Cartesian product using a list comprehension" - ] + "a, *body, c, d = range(5)\n", + "a, body, c, d" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 7, - "metadata": {}, + "execution_count": 31, "outputs": [ { "data": { - "text/plain": [ - "[('black', 'S'),\n", - " ('black', 'M'),\n", - " ('black', 'L'),\n", - " ('white', 'S'),\n", - " ('white', 'M'),\n", - " ('white', 'L')]" - ] + "text/plain": "([0, 1], 2, 3, 4)" }, - "execution_count": 7, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "colors = ['black', 'white']\n", - "sizes = ['S', 'M', 'L']\n", - "tshirts = [(color, size) for color in colors for size in sizes]\n", - "tshirts" - ] + "*head, b, c, d = range(5)\n", + "head, b, c, d" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Unpacking with * in function calls and sequence literals" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, + "execution_count": 32, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "('black', 'S')\n", - "('black', 'M')\n", - "('black', 'L')\n", - "('white', 'S')\n", - "('white', 'M')\n", - "('white', 'L')\n" - ] + "data": { + "text/plain": "(1, 2, 3, 4, (5, 6))" + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "for color in colors:\n", - " for size in sizes:\n", - " print((color, size))" - ] + "def fun(a, b, c, d, *rest):\n", + " return a, b, c, d, rest\n", + "\n", + "\n", + "fun(*[1, 2], 3, *range(4, 7))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 9, - "metadata": {}, + "execution_count": 33, "outputs": [ { "data": { - "text/plain": [ - "[('black', 'S'),\n", - " ('black', 'M'),\n", - " ('black', 'L'),\n", - " ('white', 'S'),\n", - " ('white', 'M'),\n", - " ('white', 'L')]" - ] + "text/plain": "(0, 1, 2, 3, 4)" }, - "execution_count": 9, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "shirts = [(color, size) for size in sizes\n", - " for color in colors]\n", - "tshirts" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Example 2-5. Initializing a tuple and an array from a generator expression" - ] + "*range(4), 4" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 10, - "metadata": {}, + "execution_count": 34, "outputs": [ { "data": { - "text/plain": [ - "(36, 162, 163, 165, 8364, 164)" - ] + "text/plain": "[0, 1, 2, 3, 4]" }, - "execution_count": 10, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "symbols = '$¢£¥€¤'\n", - "tuple(ord(symbol) for symbol in symbols)" - ] + "[*range(4), 4]" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, + "execution_count": 35, "outputs": [ { "data": { - "text/plain": [ - "array('I', [36, 162, 163, 165, 8364, 164])" - ] + "text/plain": "{0, 1, 2, 3, 4, 5, 6, 7}" }, - "execution_count": 11, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import array\n", - "array.array('I', (ord(symbol) for symbol in symbols))" - ] + "{*range(4), 4, *(5, 6, 7)}" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "#### Example 2-6. Cartesian product in a generator expression" - ] + "### Nested unpacking\n", + "#### Example 2-8. Unpacking nested tuples to access the longitude\n", + "\n", + "[02-array-seq/metro_lat_lon.py](02-array-seq/metro_lat_lon.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## Pattern Matching with Sequences\n", + "#### Example 2-9. Method from an imaginary Robot class" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, + "execution_count": 36, + "outputs": [], + "source": [ + "# def handle_command(self, message):\n", + "# match message:\n", + "# case ['BEEPER', frequency, times]:\n", + "# self.beep(times, frequency)\n", + "# case ['NECK', angle]:\n", + "# self.rotate_neck(angle)\n", + "# case ['LED', ident, intensity]:\n", + "# self.leds[ident].set_brightness(ident, intensity)\n", + "# case ['LED', ident, red, green, blue]:\n", + "# self.leds[ident].set_color(ident, red, green, blue)\n", + "# case _:\n", + "# raise InvalidCommand(message)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-10. Destructuring nested tuples—requires Python ≥ 3.10.\n", + "[02-array-seq/match_lat_lon.py](02-array-seq/match_lat_lon.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 37, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "black S\n", - "black M\n", - "black L\n", - "white S\n", - "white M\n", - "white L\n" + " | latitude | longitude\n", + "Mexico City | 19.4333 | -99.1333\n", + "New York-Newark | 40.8086 | -74.0204\n", + "São Paulo | -23.5478 | -46.6358\n" ] } ], "source": [ - "colors = ['black', 'white']\n", - "sizes = ['S', 'M', 'L']\n", + "metro_areas = [\n", + " ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),\n", + " ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),\n", + " ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),\n", + " ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),\n", + " ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)),\n", + "]\n", "\n", - "for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):\n", - " print(tshirt)" - ] + "def main():\n", + " print(f'{\"\":15} | {\"latitude\":>9} | {\"longitude\":>9}')\n", + " for record in metro_areas:\n", + " match record:\n", + " case [name, _, _, (lat, lon)] if lon <= 0:\n", + " print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')\n", + "main()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "### Pattern Matching Sequences in an Interpreter\n", + "#### Example 2-11. Matching patterns without match/case.\n", + "[02-array-seq/lispy/py3.9/lis.py](02-array-seq/lispy/py3.9/lis.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-12. Pattern matching with match/case—requires Python ≥ 3.10.\n", + "[02-array-seq/lispy/py3.10/lis.py](02-array-seq/lispy/py3.10/lis.py)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false + } }, { "cell_type": "markdown", @@ -375,16 +1137,14 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 38, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[10, 20]" - ] + "text/plain": "[10, 20]" }, - "execution_count": 13, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -397,16 +1157,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 39, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[30, 40, 50, 60]" - ] + "text/plain": "[30, 40, 50, 60]" }, - "execution_count": 14, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -417,16 +1175,14 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 40, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[10, 20, 30]" - ] + "text/plain": "[10, 20, 30]" }, - "execution_count": 15, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -437,16 +1193,14 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 41, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[40, 50, 60]" - ] + "text/plain": "[40, 50, 60]" }, - "execution_count": 16, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -464,16 +1218,14 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 42, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'bye'" - ] + "text/plain": "'bye'" }, - "execution_count": 17, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -485,16 +1237,14 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 43, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'elcycib'" - ] + "text/plain": "'elcycib'" }, - "execution_count": 18, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -505,16 +1255,14 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 44, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'eccb'" - ] + "text/plain": "'eccb'" }, - "execution_count": 19, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -527,12 +1275,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-9. Line items from a flat-file invoice" + "#### Example 2-13. Line items from a flat-file invoice" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 45, "metadata": {}, "outputs": [ { @@ -577,16 +1325,14 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 46, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" - ] + "text/plain": "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" }, - "execution_count": 21, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -598,16 +1344,14 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 47, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 20, 30, 5, 6, 7, 8, 9]" - ] + "text/plain": "[0, 1, 20, 30, 5, 6, 7, 8, 9]" }, - "execution_count": 22, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -619,16 +1363,14 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 48, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 20, 30, 5, 8, 9]" - ] + "text/plain": "[0, 1, 20, 30, 5, 8, 9]" }, - "execution_count": 23, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -640,16 +1382,14 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 49, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 20, 11, 5, 22, 9]" - ] + "text/plain": "[0, 1, 20, 11, 5, 22, 9]" }, - "execution_count": 24, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -668,7 +1408,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 50, "metadata": {}, "outputs": [ { @@ -688,16 +1428,14 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 51, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, 1, 100, 22, 9]" - ] + "text/plain": "[0, 1, 100, 22, 9]" }, - "execution_count": 26, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -711,21 +1449,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Using + and * with Sequences" + "## Using + and * with Sequences" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 52, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]" - ] + "text/plain": "[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]" }, - "execution_count": 27, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -737,16 +1473,14 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 53, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "'abcdabcdabcdabcdabcd'" - ] + "text/plain": "'abcdabcdabcdabcdabcd'" }, - "execution_count": 28, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -766,21 +1500,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-10. A list with three lists of length 3 can represent a tic-tac-toe board" + "#### Example 2-14. A list with three lists of length 3 can represent a tic-tac-toe board" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 54, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" }, - "execution_count": 29, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -792,16 +1524,14 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 55, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]" }, - "execution_count": 30, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -815,21 +1545,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-11. A list with three references to the same list is useless" + "#### Example 2-15. A list with three references to the same list is useless" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 56, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" }, - "execution_count": 31, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -841,16 +1569,14 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 57, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]" - ] + "text/plain": "[['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]" }, - "execution_count": 32, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -869,16 +1595,14 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 58, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]" }, - "execution_count": 33, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -893,16 +1617,14 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 59, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]" - ] + "text/plain": "[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]" }, - "execution_count": 34, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -921,7 +1643,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ @@ -931,16 +1653,14 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 61, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "4414271936" - ] + "text/plain": "140694277263808" }, - "execution_count": 36, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -952,16 +1672,14 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 62, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[1, 2, 3, 1, 2, 3]" - ] + "text/plain": "[1, 2, 3, 1, 2, 3]" }, - "execution_count": 37, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -973,16 +1691,14 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 63, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "True" - ] + "text/plain": "True" }, - "execution_count": 38, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -993,7 +1709,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 64, "metadata": {}, "outputs": [], "source": [ @@ -1003,16 +1719,14 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 65, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "4414275328" - ] + "text/plain": "140694329335488" }, - "execution_count": 40, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -1024,35 +1738,34 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 66, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "False" - ] + "text/plain": "False" }, - "execution_count": 41, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t *= 2\n", - "id(t) == idt # new tuple" + "id(t) == idt # new tuple" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### A += Assignment Puzzler" + "### A += Assignment Puzzler\n", + "#### Example 2-16. A riddle" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 67, "metadata": {}, "outputs": [ { @@ -1071,18 +1784,28 @@ " print(repr(e))" ] }, + { + "cell_type": "markdown", + "source": [ + "#### Example 2-17. The unexpected result: item t2 is changed and an exception is raised" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 68, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "(1, 2, [30, 40, 50, 60])" - ] + "text/plain": "(1, 2, [30, 40, 50, 60])" }, - "execution_count": 43, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -1095,12 +1818,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-14. Bytecode for the expression s[a] += b" + "#### Example 2-18. Bytecode for the expression s[a] += b" ] }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 69, "metadata": {}, "outputs": [ { @@ -1135,16 +1858,14 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 70, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['apple', 'banana', 'grape', 'raspberry']" - ] + "text/plain": "['apple', 'banana', 'grape', 'raspberry']" }, - "execution_count": 45, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -1156,16 +1877,14 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 71, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['grape', 'raspberry', 'apple', 'banana']" - ] + "text/plain": "['grape', 'raspberry', 'apple', 'banana']" }, - "execution_count": 46, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -1176,16 +1895,14 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 72, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['raspberry', 'grape', 'banana', 'apple']" - ] + "text/plain": "['raspberry', 'grape', 'banana', 'apple']" }, - "execution_count": 47, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -1196,16 +1913,14 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 73, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['grape', 'apple', 'banana', 'raspberry']" - ] + "text/plain": "['grape', 'apple', 'banana', 'raspberry']" }, - "execution_count": 48, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -1216,16 +1931,14 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 74, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['raspberry', 'banana', 'grape', 'apple']" - ] + "text/plain": "['raspberry', 'banana', 'grape', 'apple']" }, - "execution_count": 49, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -1236,16 +1949,14 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 75, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['grape', 'raspberry', 'apple', 'banana']" - ] + "text/plain": "['grape', 'raspberry', 'apple', 'banana']" }, - "execution_count": 50, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -1256,16 +1967,14 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 76, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['apple', 'banana', 'grape', 'raspberry']" - ] + "text/plain": "['apple', 'banana', 'grape', 'raspberry']" }, - "execution_count": 51, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } @@ -1279,322 +1988,231 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Managing Ordered Sequences with bisect" + "## When a List Is Not the Answer" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-15. bisect finds insertion points for items in a sorted sequence" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DEMO: bisect_right\n", - "haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30\n", - "31 @ 14 | | | | | | | | | | | | | |31\n", - "30 @ 14 | | | | | | | | | | | | | |30\n", - "29 @ 13 | | | | | | | | | | | | |29\n", - "23 @ 11 | | | | | | | | | | |23\n", - "22 @ 9 | | | | | | | | |22\n", - "10 @ 5 | | | | |10\n", - " 8 @ 5 | | | | |8 \n", - " 5 @ 3 | | |5 \n", - " 2 @ 1 |2 \n", - " 1 @ 1 |1 \n", - " 0 @ 0 0 \n" - ] - } - ], - "source": [ - "# BEGIN BISECT_DEMO\n", - "import bisect\n", - "import sys\n", - "\n", - "HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]\n", - "NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]\n", - "\n", - "ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}'\n", - "\n", - "def demo(haystack, needles, bisect_fn):\n", - " print('DEMO:', bisect_fn.__name__) # <1>\n", - " print('haystack ->', ' '.join('%2d' % n for n in haystack))\n", - " for needle in reversed(needles):\n", - " position = bisect_fn(haystack, needle) # <2>\n", - " offset = position * ' |' # <3>\n", - " print(ROW_FMT.format(needle, position, offset)) # <4>\n", - "\n", - "demo(HAYSTACK, NEEDLES, bisect.bisect) # <5>\n", - "# END BISECT_DEMO" - ] - }, - { - "cell_type": "code", - "execution_count": 53, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DEMO: bisect_left\n", - "haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30\n", - "31 @ 14 | | | | | | | | | | | | | |31\n", - "30 @ 13 | | | | | | | | | | | | |30\n", - "29 @ 12 | | | | | | | | | | | |29\n", - "23 @ 9 | | | | | | | | |23\n", - "22 @ 9 | | | | | | | | |22\n", - "10 @ 5 | | | | |10\n", - " 8 @ 4 | | | |8 \n", - " 5 @ 2 | |5 \n", - " 2 @ 1 |2 \n", - " 1 @ 0 1 \n", - " 0 @ 0 0 \n" - ] - } - ], - "source": [ - "demo(HAYSTACK, NEEDLES, bisect.bisect_left)" + "### Arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-16. Given a test score, grade returns the corresponding letter grade" + "#### Example 2-19. Creating, saving, and loading a large array of floats" ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 77, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['F', 'D', 'D', 'C', 'C', 'B', 'B', 'A', 'A']" - ] + "text/plain": "0.8190492979077034" }, - "execution_count": 54, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):\n", - " i = bisect.bisect(breakpoints, score)\n", - " return grades[i]\n", + "from array import array\n", + "from random import random, seed\n", + "seed(10) # Use seed to make the output consistent\n", "\n", - "[grade(score) for score in [55, 60, 65, 70, 75, 80, 85, 90, 95]]" + "floats = array('d', (random() for i in range(10 ** 7)))\n", + "floats[-1]" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 78, "metadata": {}, + "outputs": [], "source": [ - "#### Example 2-17. bisect_left maps a score of 60 to grade F, not D as in Example 2-16." + "with open('floats.bin', 'wb') as fp:\n", + " floats.tofile(fp)" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 79, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "['F', 'F', 'D', 'D', 'C', 'C', 'B', 'B', 'A']" - ] + "text/plain": "0.8190492979077034" }, - "execution_count": 55, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):\n", - " i = bisect.bisect_left(breakpoints, score)\n", - " return grades[i]\n", + "floats2 = array('d')\n", "\n", - "[grade(score) for score in [55, 60, 65, 70, 75, 80, 85, 90, 95]]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Example 2-18. Insort keeps a sorted sequence always sorted" + "with open('floats.bin', 'rb') as fp:\n", + " floats2.fromfile(fp, 10 ** 7)\n", + "\n", + "floats2[-1]" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 80, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "insert 10 -> [10]\n", - "insert 0 -> [0, 10]\n", - "insert 6 -> [0, 6, 10]\n", - "insert 8 -> [0, 6, 8, 10]\n", - "insert 7 -> [0, 6, 7, 8, 10]\n", - "insert 2 -> [0, 2, 6, 7, 8, 10]\n", - "insert 10 -> [0, 2, 6, 7, 8, 10, 10]\n" - ] + "data": { + "text/plain": "True" + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "import bisect\n", - "import random\n", - "\n", - "SIZE = 7\n", - "\n", - "random.seed(1729)\n", - "\n", - "my_list = []\n", - "\n", - "for i in range(SIZE):\n", - " new_item = random.randrange(SIZE*2)\n", - " bisect.insort(my_list, new_item)\n", - " print(f'insert {new_item:2d} -> {my_list}')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## When a List Is Not the Answer" + "floats2 == floats" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Arrays" + "### Memory Views" ] }, { "cell_type": "markdown", - "metadata": {}, "source": [ - "#### Example 2-19. Creating, saving, and loading a large array of floats" - ] + "#### Example 2-20. Handling 6 bytes memory of as 1×6, 2×3, and 3×2 views" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } }, { "cell_type": "code", - "execution_count": 57, - "metadata": {}, + "execution_count": 81, "outputs": [ { "data": { - "text/plain": [ - "0.5963321947530882" - ] + "text/plain": "[0, 1, 2, 3, 4, 5]" }, - "execution_count": 57, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "from array import array\n", - "from random import random\n", - "\n", - "floats = array('d', (random() for i in range(10**7)))\n", - "floats[-1]" - ] + "octets = array('B', range(6))\n", + "m1 = memoryview(octets)\n", + "m1.tolist()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 58, - "metadata": {}, - "outputs": [], + "execution_count": 82, + "outputs": [ + { + "data": { + "text/plain": "[[0, 1, 2], [3, 4, 5]]" + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "with open('floats.bin', 'wb') as fp:\n", - " floats.tofile(fp)" - ] + "m2 = m1.cast('B', [2, 3])\n", + "m2.tolist()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 59, - "metadata": {}, + "execution_count": 83, "outputs": [ { "data": { - "text/plain": [ - "0.5963321947530882" - ] + "text/plain": "[[0, 1], [2, 3], [4, 5]]" }, - "execution_count": 59, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "floats2 = array('d')\n", - "\n", - "with open('floats.bin', 'rb') as fp:\n", - " floats2.fromfile(fp, 10**7)\n", - "\n", - "floats2[-1]" - ] + "m3 = m1.cast('B', [3, 2])\n", + "m3.tolist()" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "code", - "execution_count": 60, - "metadata": {}, + "execution_count": 84, "outputs": [ { "data": { - "text/plain": [ - "True" - ] + "text/plain": "array('B', [0, 1, 2, 33, 22, 5])" }, - "execution_count": 60, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "floats2 == floats" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Memory Views" - ] + "m2[1,1] = 22\n", + "m3[1,1] = 33\n", + "octets" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-20. Changing the value of an array item by poking one of its bytes" + "#### Example 2-21. Changing the value of an 16-bit integer array item by poking one of its bytes" ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 85, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "5" - ] + "text/plain": "5" }, - "execution_count": 61, + "execution_count": 85, "metadata": {}, "output_type": "execute_result" } @@ -1607,16 +2225,14 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 86, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "-2" - ] + "text/plain": "-2" }, - "execution_count": 62, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } @@ -1627,16 +2243,14 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 87, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]" - ] + "text/plain": "[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]" }, - "execution_count": 63, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -1648,16 +2262,14 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 88, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array('h', [-2, -1, 1024, 1, 2])" - ] + "text/plain": "array('h', [-2, -1, 1024, 1, 2])" }, - "execution_count": 64, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } @@ -1671,50 +2283,47 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### NumPy and SciPy" + "### NumPy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-21. Basic operations with rows and columns in a numpy.ndarray" + "#### Example 2-22. Basic operations with rows and columns in a numpy.ndarray" ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 89, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])" - ] + "text/plain": "array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])" }, - "execution_count": 65, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", + "\n", "a = np.arange(12)\n", "a" ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 90, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "numpy.ndarray" - ] + "text/plain": "numpy.ndarray" }, - "execution_count": 66, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -1725,16 +2334,14 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 91, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "(12,)" - ] + "text/plain": "(12,)" }, - "execution_count": 67, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -1745,18 +2352,14 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 92, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([[ 0, 1, 2, 3],\n", - " [ 4, 5, 6, 7],\n", - " [ 8, 9, 10, 11]])" - ] + "text/plain": "array([[ 0, 1, 2, 3],\n [ 4, 5, 6, 7],\n [ 8, 9, 10, 11]])" }, - "execution_count": 68, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -1768,16 +2371,14 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 93, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([ 8, 9, 10, 11])" - ] + "text/plain": "array([ 8, 9, 10, 11])" }, - "execution_count": 69, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -1788,16 +2389,14 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 94, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "9" - ] + "text/plain": "9" }, - "execution_count": 70, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -1808,16 +2407,14 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 95, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([1, 5, 9])" - ] + "text/plain": "array([1, 5, 9])" }, - "execution_count": 71, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -1828,25 +2425,20 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 96, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([[ 0, 4, 8],\n", - " [ 1, 5, 9],\n", - " [ 2, 6, 10],\n", - " [ 3, 7, 11]])" - ] + "text/plain": "array([[ 0, 4, 8],\n [ 1, 5, 9],\n [ 2, 6, 10],\n [ 3, 7, 11]])" }, - "execution_count": 72, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "a.transpose()\n" + "a.transpose()" ] }, { @@ -1858,7 +2450,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 97, "metadata": {}, "outputs": [], "source": [ @@ -1869,7 +2461,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 98, "metadata": {}, "outputs": [], "source": [ @@ -1878,16 +2470,14 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 99, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([0.29150425, 0.33893554, 0.08112756])" - ] + "text/plain": "array([0.06078257, 0.61741189, 0.84349987])" }, - "execution_count": 75, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -1898,16 +2488,14 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 100, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "array([0.14575213, 0.16946777, 0.04056378])" - ] + "text/plain": "array([0.03039128, 0.30870594, 0.42174994])" }, - "execution_count": 76, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -1919,16 +2507,14 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 101, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "True" - ] + "text/plain": "True" }, - "execution_count": 77, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -1943,7 +2529,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 102, "metadata": {}, "outputs": [], "source": [ @@ -1954,16 +2540,14 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 103, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "memmap([0.29150425, 0.33893554, 0.08112756])" - ] + "text/plain": "memmap([0.06078257, 0.61741189, 0.84349987])" }, - "execution_count": 79, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -1983,21 +2567,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Example 2-22. Working with a deque" + "#### Example 2-23. Working with a deque" ] }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 104, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" - ] + "text/plain": "deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" }, - "execution_count": 80, + "execution_count": 104, "metadata": {}, "output_type": "execute_result" } @@ -2011,16 +2593,14 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 105, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])" - ] + "text/plain": "deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])" }, - "execution_count": 81, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -2032,16 +2612,14 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 106, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])" - ] + "text/plain": "deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])" }, - "execution_count": 82, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } @@ -2053,16 +2631,14 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 107, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])" - ] + "text/plain": "deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])" }, - "execution_count": 83, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } @@ -2074,16 +2650,14 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 108, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])" - ] + "text/plain": "deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])" }, - "execution_count": 84, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } @@ -2095,16 +2669,14 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 109, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])" - ] + "text/plain": "deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])" }, - "execution_count": 85, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } @@ -2130,7 +2702,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 110, "metadata": {}, "outputs": [], "source": [ @@ -2139,7 +2711,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 111, "metadata": {}, "outputs": [ { @@ -2166,16 +2738,14 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 112, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, '1', 5, 6, '9', 14, 19, '23', 28, '28']" - ] + "text/plain": "[0, '1', 5, 6, '9', 14, 19, '23', 28, '28']" }, - "execution_count": 88, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } @@ -2188,16 +2758,14 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 113, "metadata": {}, "outputs": [ { "data": { - "text/plain": [ - "[0, '1', 14, 19, '23', 28, '28', 5, 6, '9']" - ] + "text/plain": "[0, '1', 14, 19, '23', 28, '28', 5, 6, '9']" }, - "execution_count": 89, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -2208,7 +2776,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 113, "metadata": {}, "outputs": [], "source": [] @@ -2235,4 +2803,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/02-array-seq/lispy/py3.10/examples_test.py b/02-array-seq/lispy/py3.10/examples_test.py index ba6bd10..603ccd3 100644 --- a/02-array-seq/lispy/py3.10/examples_test.py +++ b/02-array-seq/lispy/py3.10/examples_test.py @@ -147,7 +147,7 @@ def test_factorial(): gcd_src = """ (define (mod m n) - (- m (* n (// m n)))) + (- m (* n (quotient m n)))) (define (gcd m n) (if (= n 0) m diff --git a/02-array-seq/lispy/py3.10/lis.py b/02-array-seq/lispy/py3.10/lis.py index 150fca7..88d9345 100755 --- a/02-array-seq/lispy/py3.10/lis.py +++ b/02-array-seq/lispy/py3.10/lis.py @@ -84,7 +84,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, @@ -149,7 +149,7 @@ def evaluate(exp: Expression, env: Environment) -> Any: # end::EVAL_MATCH_TOP[] case int(x) | float(x): return x - case Symbol(name): + case Symbol() as name: return env[name] # tag::EVAL_MATCH_MIDDLE[] case ['quote', x]: # <1> @@ -161,12 +161,12 @@ def evaluate(exp: Expression, env: Environment) -> Any: return evaluate(alternative, env) case ['lambda', [*parms], *body] if body: # <3> return Procedure(parms, body, env) - case ['define', Symbol(name), value_exp]: # <4> + case ['define', Symbol() as name, value_exp]: # <4> env[name] = evaluate(value_exp, env) # end::EVAL_MATCH_MIDDLE[] - case ['define', [Symbol(name), *parms], *body] if body: + case ['define', [Symbol() as name, *parms], *body] if body: env[name] = Procedure(parms, body, env) - case ['set!', Symbol(name), value_exp]: + case ['set!', Symbol() as name, value_exp]: env.change(name, evaluate(value_exp, env)) case [func_exp, *args] if func_exp not in KEYWORDS: proc = evaluate(func_exp, env) diff --git a/02-array-seq/lispy/py3.9/lis.py b/02-array-seq/lispy/py3.9/lis.py index 201b41e..dd80096 100644 --- a/02-array-seq/lispy/py3.9/lis.py +++ b/02-array-seq/lispy/py3.9/lis.py @@ -80,7 +80,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, diff --git a/02-array-seq/test.sh b/02-array-seq/test.sh index 4747320..906717d 100755 --- a/02-array-seq/test.sh +++ b/02-array-seq/test.sh @@ -1,4 +1,4 @@ #!/bin/bash python3 -m doctest bisect_demo.py -python3 -m doctest metro_lat_long.py +python3 -m doctest metro_lat_lon.py pytest -q --nbval diff --git a/06-obj-ref/README.rst b/06-obj-ref/README.rst index deac2fa..0d705e6 100644 --- a/06-obj-ref/README.rst +++ b/06-obj-ref/README.rst @@ -1,4 +1,4 @@ -Sample code for Chapter 8 - "Object references, mutability and recycling" +Sample code for Chapter 6 - "Object references, mutability and recycling" From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015) http://shop.oreilly.com/product/0636920032519.do diff --git a/08-def-type-hints/callable/variance.py b/08-def-type-hints/callable/variance.py new file mode 100644 index 0000000..fcddbde --- /dev/null +++ b/08-def-type-hints/callable/variance.py @@ -0,0 +1,22 @@ +from collections.abc import Callable + +def update( # <1> + probe: Callable[[], float], # <2> + display: Callable[[float], None] # <3> + ) -> None: + temperature = probe() + # imagine lots of control code here + display(temperature) + +def probe_ok() -> int: # <4> + return 42 + +def display_wrong(temperature: int) -> None: # <5> + print(hex(temperature)) + +update(probe_ok, display_wrong) # type error # <6> + +def display_ok(temperature: complex) -> None: # <7> + print(temperature) + +update(probe_ok, display_ok) # OK # <8> diff --git a/08-def-type-hints/coordinates/requirements.txt b/08-def-type-hints/coordinates/requirements.txt index fb11094..7b7b424 100644 --- a/08-def-type-hints/coordinates/requirements.txt +++ b/08-def-type-hints/coordinates/requirements.txt @@ -1,2 +1,2 @@ geolib==1.0.7 -future==0.18.2 +future==0.18.3 diff --git a/11-pythonic-obj/vector2d_v3.py b/11-pythonic-obj/vector2d_v3.py index 9ee716c..0edffb7 100644 --- a/11-pythonic-obj/vector2d_v3.py +++ b/11-pythonic-obj/vector2d_v3.py @@ -79,8 +79,6 @@ >>> v1 = Vector2d(3, 4) >>> v2 = Vector2d(3.1, 4.2) - >>> hash(v1), hash(v2) - (7, 384307168202284039) >>> len({v1, v2}) 2 @@ -124,7 +122,7 @@ def __eq__(self, other): return tuple(self) == tuple(other) def __hash__(self): - return hash(self.x) ^ hash(self.y) + return hash((self.x, self.y)) def __abs__(self): return math.hypot(self.x, self.y) diff --git a/11-pythonic-obj/vector2d_v3_prophash.py b/11-pythonic-obj/vector2d_v3_prophash.py index 6d7dceb..1652cd4 100644 --- a/11-pythonic-obj/vector2d_v3_prophash.py +++ b/11-pythonic-obj/vector2d_v3_prophash.py @@ -81,8 +81,6 @@ >>> v1 = Vector2d(3, 4) >>> v2 = Vector2d(3.1, 4.2) - >>> hash(v1), hash(v2) - (7, 384307168202284039) >>> len({v1, v2}) 2 @@ -131,7 +129,7 @@ def __eq__(self, other): # tag::VECTOR_V3_HASH[] def __hash__(self): - return hash(self.x) ^ hash(self.y) + return hash((self.x, self.y)) # end::VECTOR_V3_HASH[] def __abs__(self): diff --git a/11-pythonic-obj/vector2d_v3_slots.py b/11-pythonic-obj/vector2d_v3_slots.py index c48fb5b..89da973 100644 --- a/11-pythonic-obj/vector2d_v3_slots.py +++ b/11-pythonic-obj/vector2d_v3_slots.py @@ -78,8 +78,6 @@ >>> v1 = Vector2d(3, 4) >>> v2 = Vector2d(3.1, 4.2) - >>> hash(v1), hash(v2) - (7, 384307168202284039) >>> len({v1, v2}) 2 @@ -126,7 +124,7 @@ def __eq__(self, other): return tuple(self) == tuple(other) def __hash__(self): - return hash(self.x) ^ hash(self.y) + return hash((self.x, self.y)) def __abs__(self): return math.hypot(self.x, self.y) diff --git a/17-it-generator/columnize_iter.py b/17-it-generator/columnize_iter.py index cee0f13..4362bc1 100644 --- a/17-it-generator/columnize_iter.py +++ b/17-it-generator/columnize_iter.py @@ -6,8 +6,8 @@ def columnize( ) -> Iterator[tuple[str, ...]]: # <1> if num_columns == 0: num_columns = round(len(sequence) ** 0.5) - num_rows, reminder = divmod(len(sequence), num_columns) - num_rows += bool(reminder) + num_rows, remainder = divmod(len(sequence), num_columns) + num_rows += bool(remainder) return (tuple(sequence[i::num_rows]) for i in range(num_rows)) # <2> # end::COLUMNIZE[] diff --git a/17-it-generator/tree/classtree/classtree.py b/17-it-generator/tree/classtree/classtree.py new file mode 100755 index 0000000..56bbec3 --- /dev/null +++ b/17-it-generator/tree/classtree/classtree.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +from importlib import import_module +import sys + + +SP = '\N{SPACE}' +HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' * 2 + SP # ── +VLIN = '\N{BOX DRAWINGS LIGHT VERTICAL}' + SP * 3 # │ +TEE = '\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}' + HLIN # ├── +ELBOW = '\N{BOX DRAWINGS LIGHT UP AND RIGHT}' + HLIN # └── + + +def subclasses(cls): + try: + return cls.__subclasses__() + except TypeError: # handle the `type` type + return cls.__subclasses__(cls) + + +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling + chidren = subclasses(cls) + if chidren: + last = chidren[-1] + for child in chidren: + yield from tree(child, level + 1, child is last) + + +def render_lines(tree_generator): + cls, _, _ = next(tree_generator) + yield cls.__name__ + prefix = '' + for cls, level, last in tree_generator: + prefix = prefix[: 4 * (level - 1)] + prefix = prefix.replace(TEE, VLIN).replace(ELBOW, SP * 4) + prefix += ELBOW if last else TEE + yield prefix + cls.__name__ + + +def draw(cls): + for line in render_lines(tree(cls)): + print(line) + + +def parse(name): + if '.' in name: + return name.rsplit('.', 1) + else: + return 'builtins', name + + +def main(name): + module_name, cls_name = parse(name) + try: + cls = getattr(import_module(module_name), cls_name) + except ModuleNotFoundError: + print(f'*** Could not import {module_name!r}.') + except AttributeError: + print(f'*** {cls_name!r} not found in {module_name!r}.') + else: + if isinstance(cls, type): + draw(cls) + else: + print(f'*** {cls_name!r} is not a class.') + + +if __name__ == '__main__': + if len(sys.argv) == 2: + main(sys.argv[1]) + else: + print('Usage:' + f'\t{sys.argv[0]} Class # for builtin classes\n' + f'\t{sys.argv[0]} package.Class # for other classes' + ) diff --git a/17-it-generator/tree/classtree/classtree_test.py b/17-it-generator/tree/classtree/classtree_test.py new file mode 100644 index 0000000..4535747 --- /dev/null +++ b/17-it-generator/tree/classtree/classtree_test.py @@ -0,0 +1,181 @@ +from textwrap import dedent +from typing import SupportsBytes +from classtree import tree, render_lines, main, subclasses + +from abc import ABCMeta + + +def test_subclasses(): + result = subclasses(UnicodeError) + assert set(result) >= {UnicodeEncodeError, UnicodeDecodeError} + + +def test_subclasses_of_type(): + """ + The `type` class is a special case because `type.__subclasses__()` + is an unbound method when called on it, so we must call it as + `type.__subclasses__(type)` just for `type`. + + This test does not verify the full list of results, but just + checks that `abc.ABCMeta` is included, because that's the only + subclass of `type` (i.e. metaclass) I we get when I run + `$ classtree.py type` at the command line. + + However, the Python console and `pytest` both load other modules, + so `subclasses` may find more subclasses of `type`—for example, + `enum.EnumMeta`. + """ + result = subclasses(type) + assert ABCMeta in result + + +def test_tree_1_level(): + result = list(tree(TabError)) + assert result == [(TabError, 0, True)] + + +def test_tree_2_levels(): + result = list(tree(IndentationError)) + assert result == [ + (IndentationError, 0, True), + (TabError, 1, True), + ] + + +def test_render_lines_1_level(): + result = list(render_lines(tree(TabError))) + assert result == ['TabError'] + + +def test_render_lines_2_levels_1_leaf(): + result = list(render_lines(tree(IndentationError))) + expected = [ + 'IndentationError', + '└── TabError', + ] + assert expected == result + + +def test_render_lines_3_levels_1_leaf(): + class X: pass + class Y(X): pass + class Z(Y): pass + result = list(render_lines(tree(X))) + expected = [ + 'X', + '└── Y', + ' └── Z', + ] + assert expected == result + + +def test_render_lines_4_levels_1_leaf(): + class Level0: pass + class Level1(Level0): pass + class Level2(Level1): pass + class Level3(Level2): pass + + result = list(render_lines(tree(Level0))) + expected = [ + 'Level0', + '└── Level1', + ' └── Level2', + ' └── Level3', + ] + assert expected == result + + +def test_render_lines_2_levels_2_leaves(): + class Branch: pass + class Leaf1(Branch): pass + class Leaf2(Branch): pass + result = list(render_lines(tree(Branch))) + expected = [ + 'Branch', + '├── Leaf1', + '└── Leaf2', + ] + assert expected == result + + +def test_render_lines_3_levels_2_leaves_dedent(): + class A: pass + class B(A): pass + class C(B): pass + class D(A): pass + class E(D): pass + + result = list(render_lines(tree(A))) + expected = [ + 'A', + '├── B', + '│ └── C', + '└── D', + ' └── E', + ] + assert expected == result + + +def test_render_lines_4_levels_4_leaves_dedent(): + class A: pass + class B1(A): pass + class C1(B1): pass + class D1(C1): pass + class D2(C1): pass + class C2(B1): pass + class B2(A): pass + expected = [ + 'A', + '├── B1', + '│ ├── C1', + '│ │ ├── D1', + '│ │ └── D2', + '│ └── C2', + '└── B2', + ] + + result = list(render_lines(tree(A))) + assert expected == result + + +def test_main_simple(capsys): + main('IndentationError') + expected = dedent(""" + IndentationError + └── TabError + """).lstrip() + captured = capsys.readouterr() + assert captured.out == expected + + +def test_main_dotted(capsys): + main('collections.abc.Sequence') + expected = dedent(""" + Sequence + ├── ByteString + ├── MutableSequence + │ └── UserList + """).lstrip() + captured = capsys.readouterr() + assert captured.out.startswith(expected) + + +def test_main_class_not_found(capsys): + main('NoSuchClass') + expected = "*** 'NoSuchClass' not found in 'builtins'.\n" + captured = capsys.readouterr() + assert captured.out == expected + + +def test_main_module_not_found(capsys): + main('nosuch.module') + expected = "*** Could not import 'nosuch'.\n" + captured = capsys.readouterr() + assert captured.out == expected + + +def test_main_not_a_class(capsys): + main('collections.abc') + expected = "*** 'abc' is not a class.\n" + captured = capsys.readouterr() + assert captured.out == expected diff --git a/17-it-generator/tree/extra/drawtree.py b/17-it-generator/tree/extra/drawtree.py new file mode 100644 index 0000000..b74cd33 --- /dev/null +++ b/17-it-generator/tree/extra/drawtree.py @@ -0,0 +1,44 @@ +from tree import tree + +SP = '\N{SPACE}' +HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' * 2 + SP # ── +VLIN = '\N{BOX DRAWINGS LIGHT VERTICAL}' + SP * 3 # │ +TEE = '\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}' + HLIN # ├── +ELBOW = '\N{BOX DRAWINGS LIGHT UP AND RIGHT}' + HLIN # └── + + +def subclasses(cls): + try: + return cls.__subclasses__() + except TypeError: # handle the `type` type + return cls.__subclasses__(cls) + + +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling + children = subclasses(cls) + if children: + last = children[-1] + for child in children: + yield from tree(child, level+1, child is last) + + +def render_lines(tree_iter): + cls, _, _ = next(tree_iter) + yield cls.__name__ + prefix = '' + + for cls, level, last in tree_iter: + prefix = prefix[:4 * (level - 1)] + prefix = prefix.replace(TEE, VLIN).replace(ELBOW, SP * 4) + prefix += ELBOW if last else TEE + yield prefix + cls.__name__ + + +def draw(cls): + for line in render_lines(tree(cls)): + print(line) + + +if __name__ == '__main__': + draw(BaseException) diff --git a/17-it-generator/tree/extra/pretty_tree.py b/17-it-generator/tree/extra/pretty_tree.py deleted file mode 100644 index 6d6d91a..0000000 --- a/17-it-generator/tree/extra/pretty_tree.py +++ /dev/null @@ -1,35 +0,0 @@ -from tree import tree - -SPACES = ' ' * 4 -HLINE = '\u2500' # ─ BOX DRAWINGS LIGHT HORIZONTAL -HLINE2 = HLINE * 2 -ELBOW = f'\u2514{HLINE2} ' # └ BOX DRAWINGS LIGHT UP AND RIGHT -TEE = f'\u251C{HLINE2} ' # ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT -PIPE = '\u2502 ' # │ BOX DRAWINGS LIGHT VERTICAL - - -def render_lines(tree_iter): - name, _, _ = next(tree_iter) - yield name - prefix = '' - - for name, level, last in tree_iter: - if last: - connector = ELBOW - else: - connector = TEE - - prefix = prefix[:4 * (level-1)] - prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SPACES) - prefix += connector - - yield prefix + name - - -def display(cls): - for line in render_lines(tree(cls)): - print(line) - - -if __name__ == '__main__': - display(BaseException) diff --git a/17-it-generator/tree/extra/test_pretty_tree.py b/17-it-generator/tree/extra/test_drawtree.py similarity index 94% rename from 17-it-generator/tree/extra/test_pretty_tree.py rename to 17-it-generator/tree/extra/test_drawtree.py index 70019a3..f66dfa4 100644 --- a/17-it-generator/tree/extra/test_pretty_tree.py +++ b/17-it-generator/tree/extra/test_drawtree.py @@ -1,4 +1,4 @@ -from pretty_tree import tree, render_lines +from drawtree import tree, render_lines def test_1_level(): result = list(render_lines(tree(BrokenPipeError))) @@ -59,7 +59,7 @@ class Leaf2(Branch): pass assert expected == result -def test_3_levels_2_leaves(): +def test_3_levels_2_leaves_dedent(): class A: pass class B(A): pass class C(B): pass @@ -77,7 +77,7 @@ class E(D): pass assert expected == result -def test_4_levels_4_leaves(): +def test_4_levels_4_leaves_dedent(): class A: pass class B1(A): pass class C1(B1): pass diff --git a/17-it-generator/tree/extra/test_tree.py b/17-it-generator/tree/extra/test_tree.py index 4c6404c..b06cf3b 100644 --- a/17-it-generator/tree/extra/test_tree.py +++ b/17-it-generator/tree/extra/test_tree.py @@ -4,7 +4,8 @@ def test_1_level(): class One: pass expected = [('One', 0, True)] - result = list(tree(One)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(One)] assert expected == result @@ -17,7 +18,8 @@ class Leaf2(Branch): pass ('Leaf1', 1, False), ('Leaf2', 1, True), ] - result = list(tree(Branch)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(Branch)] assert expected == result @@ -30,7 +32,8 @@ class Z(Y): pass ('Y', 1, True), ('Z', 2, True), ] - result = list(tree(X)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(X)] assert expected == result @@ -46,7 +49,8 @@ class Level3(Level2): pass ('Level3', 3, True), ] - result = list(tree(Level0)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(Level0)] assert expected == result @@ -68,7 +72,8 @@ class D2(C1): pass ('C2', 2, True), ] - result = list(tree(A)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(A)] assert expected == result @@ -83,7 +88,8 @@ class Root: pass expected.append((name, level, True)) parent = cls - result = list(tree(Root)) + result = [(cls.__name__, level, last) + for cls, level, last in tree(Root)] assert len(result) == level_count assert result[0] == ('Root', 0, True) assert result[-1] == ('Sub99', 99, True) diff --git a/17-it-generator/tree/extra/tree.py b/17-it-generator/tree/extra/tree.py index 7169a28..aeebd37 100644 --- a/17-it-generator/tree/extra/tree.py +++ b/17-it-generator/tree/extra/tree.py @@ -1,5 +1,5 @@ -def tree(cls, level=0, last_in_level=True): - yield cls.__name__, level, last_in_level +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling subclasses = cls.__subclasses__() if subclasses: last = subclasses[-1] @@ -8,9 +8,9 @@ def tree(cls, level=0, last_in_level=True): def display(cls): - for cls_name, level, _ in tree(cls): + for cls, level, _ in tree(cls): indent = ' ' * 4 * level - print(f'{indent}{cls_name}') + print(f'{indent}{cls.__name__}') if __name__ == '__main__': diff --git a/18-with-match/lispy/py3.10/examples_test.py b/18-with-match/lispy/py3.10/examples_test.py index 2807596..7025dad 100644 --- a/18-with-match/lispy/py3.10/examples_test.py +++ b/18-with-match/lispy/py3.10/examples_test.py @@ -27,11 +27,13 @@ >>> inner_env = {'a': 2} >>> outer_env = {'a': 0, 'b': 1} >>> env = Environment(inner_env, outer_env) ->>> env['a'] = 111 # <1> +>>> env['a'] # <1> +2 +>>> env['a'] = 111 # <2> >>> env['c'] = 222 >>> env Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 1}) ->>> env.change('b', 333) # <2> +>>> env.change('b', 333) # <3> >>> env Environment({'a': 111, 'c': 222}, {'a': 0, 'b': 333}) @@ -146,7 +148,7 @@ def test_factorial(): gcd_src = """ (define (mod m n) - (- m (* n (// m n)))) + (- m (* n (quotient m n)))) (define (gcd m n) (if (= n 0) m diff --git a/18-with-match/lispy/py3.10/lis.py b/18-with-match/lispy/py3.10/lis.py index 0faf6d2..f2d982c 100755 --- a/18-with-match/lispy/py3.10/lis.py +++ b/18-with-match/lispy/py3.10/lis.py @@ -65,7 +65,7 @@ def parse_atom(token: str) -> Atom: class Environment(ChainMap[Symbol, Any]): "A ChainMap that allows changing an item in-place." - def change(self, key: Symbol, value: object) -> None: + def change(self, key: Symbol, value: Any) -> None: "Find where key is defined and change the value there." for map in self.maps: if key in map: @@ -83,7 +83,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, diff --git a/18-with-match/lispy/py3.9/examples_test.py b/18-with-match/lispy/py3.9/examples_test.py index c632662..603ccd3 100644 --- a/18-with-match/lispy/py3.9/examples_test.py +++ b/18-with-match/lispy/py3.9/examples_test.py @@ -147,7 +147,7 @@ def test_factorial(): gcd_src = """ (define (mod m n) - (- m (* n (// m n)))) + (- m (* n (quotient m n)))) (define (gcd m n) (if (= n 0) m @@ -255,4 +255,4 @@ def test_closure_with_change(capsys): def test_closure_averager(): got = run(closure_averager_src) assert got == 12.0 -# end::RUN_AVERAGER[] \ No newline at end of file +# end::RUN_AVERAGER[] diff --git a/18-with-match/lispy/py3.9/lis.py b/18-with-match/lispy/py3.9/lis.py index 6ab8a19..e467211 100644 --- a/18-with-match/lispy/py3.9/lis.py +++ b/18-with-match/lispy/py3.9/lis.py @@ -62,7 +62,7 @@ def parse_atom(token: str) -> Atom: class Environment(ChainMap[Symbol, Any]): "A ChainMap that allows changing an item in-place." - def change(self, key: Symbol, value: object) -> None: + def change(self, key: Symbol, value: Any) -> None: "Find where key is defined and change the value there." for map in self.maps: if key in map: @@ -80,7 +80,7 @@ def standard_env() -> Environment: '-': op.sub, '*': op.mul, '/': op.truediv, - '//': op.floordiv, + 'quotient': op.floordiv, '>': op.gt, '<': op.lt, '>=': op.ge, diff --git a/19-concurrency/primes/procs.py b/19-concurrency/primes/procs.py index 09f75e3..50e3d9f 100644 --- a/19-concurrency/primes/procs.py +++ b/19-concurrency/primes/procs.py @@ -31,46 +31,46 @@ def worker(jobs: JobQueue, results: ResultQueue) -> None: # <7> while n := jobs.get(): # <8> results.put(check(n)) # <9> results.put(PrimeResult(0, False, 0.0)) # <10> -# end::PRIMES_PROC_TOP[] -# tag::PRIMES_PROC_MIDDLE[] -def start_jobs(workers: int, jobs: JobQueue, results: ResultQueue) -> None: +def start_jobs( + procs: int, jobs: JobQueue, results: ResultQueue # <11> +) -> None: for n in NUMBERS: - jobs.put(n) # <1> - for _ in range(workers): - proc = Process(target=worker, args=(jobs, results)) # <2> - proc.start() # <3> - jobs.put(0) # <4> - -def report(workers: int, results: ResultQueue) -> int: - checked = 0 - workers_done = 0 - while workers_done < workers: - n, prime, elapsed = results.get() - if n == 0: - workers_done += 1 - else: - checked += 1 - label = 'P' if prime else ' ' - print(f'{n:16} {label} {elapsed:9.6f}s') - return checked -# end::PRIMES_PROC_MIDDLE[] + jobs.put(n) # <12> + for _ in range(procs): + proc = Process(target=worker, args=(jobs, results)) # <13> + proc.start() # <14> + jobs.put(0) # <15> +# end::PRIMES_PROC_TOP[] # tag::PRIMES_PROC_MAIN[] def main() -> None: - if len(sys.argv) < 2: - workers = cpu_count() + if len(sys.argv) < 2: # <1> + procs = cpu_count() else: - workers = int(sys.argv[1]) + procs = int(sys.argv[1]) - print(f'Checking {len(NUMBERS)} numbers with {workers} processes:') + print(f'Checking {len(NUMBERS)} numbers with {procs} processes:') t0 = perf_counter() - jobs: JobQueue = SimpleQueue() + jobs: JobQueue = SimpleQueue() # <2> results: ResultQueue = SimpleQueue() - start_jobs(workers, jobs, results) - checked = report(workers, results) + start_jobs(procs, jobs, results) # <3> + checked = report(procs, results) # <4> elapsed = perf_counter() - t0 - print(f'{checked} checks in {elapsed:.2f}s') + print(f'{checked} checks in {elapsed:.2f}s') # <5> + +def report(procs: int, results: ResultQueue) -> int: # <6> + checked = 0 + procs_done = 0 + while procs_done < procs: # <7> + n, prime, elapsed = results.get() # <8> + if n == 0: # <9> + procs_done += 1 + else: + checked += 1 # <10> + label = 'P' if prime else ' ' + print(f'{n:16} {label} {elapsed:9.6f}s') + return checked if __name__ == '__main__': main() diff --git a/19-concurrency/primes/procs_race_condition.py b/19-concurrency/primes/procs_race_condition.py new file mode 100755 index 0000000..aa5e172 --- /dev/null +++ b/19-concurrency/primes/procs_race_condition.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +""" +procs.py: shows that multiprocessing on a multicore machine +can be faster than sequential code for CPU-intensive work. +""" + +# tag::PRIMES_PROC_TOP[] +import sys +from time import perf_counter +from typing import NamedTuple +from multiprocessing import Process, SimpleQueue, cpu_count # <1> +from multiprocessing import queues # <2> + +from primes import is_prime, NUMBERS + +class PrimeResult(NamedTuple): # <3> + n: int + prime: bool + elapsed: float + +JobQueue = queues.SimpleQueue[int] # <4> +ResultQueue = queues.SimpleQueue[PrimeResult] # <5> + +def check(n: int) -> PrimeResult: # <6> + t0 = perf_counter() + res = is_prime(n) + return PrimeResult(n, res, perf_counter() - t0) + +def worker(jobs: JobQueue, results: ResultQueue) -> None: # <7> + while n := jobs.get(): # <8> + results.put(check(n)) # <9> + results.put(PrimeResult(0, False, 0.0)) +# end::PRIMES_PROC_TOP[] + +def start_jobs(workers: int) -> ResultQueue: + jobs: JobQueue = SimpleQueue() # <2> + results: ResultQueue = SimpleQueue() + + for n in NUMBERS: # <3> + jobs.put(n) + + for _ in range(workers): + proc = Process(target=worker, args=(jobs, results)) # <4> + proc.start() # <5> + jobs.put(0) # <6> + + return results + +def report(workers: int, results: ResultQueue) -> int: + workers_done = 0 + checked = 0 + while workers_done < workers: + n, prime, elapsed = results.get() # <7> + if n == 0: + workers_done += 1 + else: + checked += 1 + label = 'P' if prime else ' ' + print(f'{n:16} {label} {elapsed:9.6f}s') # <8> + return checked + + +# tag::PRIMES_PROC_MAIN[] +def main() -> None: + if len(sys.argv) < 2: # <1> + workers = cpu_count() + else: + workers = int(sys.argv[1]) + + print(f'Checking {len(NUMBERS)} numbers with {workers} processes:') + t0 = perf_counter() + results = start_jobs(workers) + checked = report(workers, results) + elapsed = perf_counter() - t0 + print(f'{checked} checks in {elapsed:.2f}s') + +if __name__ == '__main__': + main() +# end::PRIMES_PROC_MAIN[] diff --git a/20-futures/demo_executor_map.py b/20-executors/demo_executor_map.py similarity index 100% rename from 20-futures/demo_executor_map.py rename to 20-executors/demo_executor_map.py diff --git a/20-executors/getflags/.gitignore b/20-executors/getflags/.gitignore new file mode 100644 index 0000000..aad8463 --- /dev/null +++ b/20-executors/getflags/.gitignore @@ -0,0 +1,2 @@ +flags/ +downloaded/ diff --git a/20-futures/getflags/README.adoc b/20-executors/getflags/README.adoc similarity index 100% rename from 20-futures/getflags/README.adoc rename to 20-executors/getflags/README.adoc diff --git a/20-futures/getflags/country_codes.txt b/20-executors/getflags/country_codes.txt similarity index 100% rename from 20-futures/getflags/country_codes.txt rename to 20-executors/getflags/country_codes.txt diff --git a/20-futures/getflags/flags.py b/20-executors/getflags/flags.py similarity index 100% rename from 20-futures/getflags/flags.py rename to 20-executors/getflags/flags.py diff --git a/20-futures/getflags/flags.zip b/20-executors/getflags/flags.zip similarity index 100% rename from 20-futures/getflags/flags.zip rename to 20-executors/getflags/flags.zip diff --git a/20-futures/getflags/flags2_asyncio.py b/20-executors/getflags/flags2_asyncio.py similarity index 66% rename from 20-futures/getflags/flags2_asyncio.py rename to 20-executors/getflags/flags2_asyncio.py index 4825db4..b697840 100755 --- a/20-futures/getflags/flags2_asyncio.py +++ b/20-executors/getflags/flags2_asyncio.py @@ -16,37 +16,36 @@ from flags2_common import main, DownloadStatus, save_flag -# default set low to avoid errors from remote site, such as -# 503 - Service Temporarily Unavailable +# low concurrency default to avoid errors from remote site, +# such as 503 - Service Temporarily Unavailable DEFAULT_CONCUR_REQ = 5 MAX_CONCUR_REQ = 1000 -async def get_flag(session: httpx.AsyncClient, # <2> +async def get_flag(client: httpx.AsyncClient, # <1> base_url: str, cc: str) -> bytes: url = f'{base_url}/{cc}/{cc}.gif'.lower() - resp = await session.get(url, timeout=3.1, follow_redirects=True) # <3> + resp = await client.get(url, timeout=3.1, follow_redirects=True) # <2> resp.raise_for_status() return resp.content -async def download_one(session: httpx.AsyncClient, +async def download_one(client: httpx.AsyncClient, cc: str, base_url: str, - semaphore: asyncio.Semaphore, # <4> + semaphore: asyncio.Semaphore, verbose: bool) -> DownloadStatus: try: - async with semaphore: # <5> - image = await get_flag(session, base_url, cc) + async with semaphore: # <3> + image = await get_flag(client, base_url, cc) except httpx.HTTPStatusError as exc: # <4> res = exc.response if res.status_code == HTTPStatus.NOT_FOUND: - status = DownloadStatus.NOT_FOUND # <5> + status = DownloadStatus.NOT_FOUND msg = f'not found: {res.url}' else: raise - else: - await asyncio.to_thread(save_flag, image, f'{cc}.gif') + await asyncio.to_thread(save_flag, image, f'{cc}.gif') # <5> status = DownloadStatus.OK msg = 'OK' if verbose and msg: @@ -61,33 +60,33 @@ async def supervisor(cc_list: list[str], concur_req: int) -> Counter[DownloadStatus]: # <1> counter: Counter[DownloadStatus] = Counter() semaphore = asyncio.Semaphore(concur_req) # <2> - async with httpx.AsyncClient() as session: - to_do = [download_one(session, cc, base_url, semaphore, verbose) + async with httpx.AsyncClient() as client: + to_do = [download_one(client, cc, base_url, semaphore, verbose) for cc in sorted(cc_list)] # <3> to_do_iter = asyncio.as_completed(to_do) # <4> if not verbose: to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> - error: httpx.HTTPError | None = None - for coro in to_do_iter: # <6> + error: httpx.HTTPError | None = None # <6> + for coro in to_do_iter: # <7> try: - status = await coro # <7> - except httpx.HTTPStatusError as exc: # <8> + status = await coro # <8> + except httpx.HTTPStatusError as exc: error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' error_msg = error_msg.format(resp=exc.response) - error = exc - except httpx.RequestError as exc: # <9> + error = exc # <9> + except httpx.RequestError as exc: error_msg = f'{exc} {type(exc)}'.strip() - error = exc - except KeyboardInterrupt: # <10> + error = exc # <10> + except KeyboardInterrupt: break - else: # <11> + else: error = None if error: - status = DownloadStatus.ERROR # <12> + status = DownloadStatus.ERROR # <11> if verbose: - url = str(error.request.url) # <13> - cc = Path(url).stem.upper() # <14> + url = str(error.request.url) # <12> + cc = Path(url).stem.upper() # <13> print(f'{cc} error: {error_msg}') counter[status] += 1 diff --git a/20-futures/getflags/flags2_asyncio_executor.py b/20-executors/getflags/flags2_asyncio_executor.py similarity index 90% rename from 20-futures/getflags/flags2_asyncio_executor.py rename to 20-executors/getflags/flags2_asyncio_executor.py index 752b60f..62516b3 100755 --- a/20-futures/getflags/flags2_asyncio_executor.py +++ b/20-executors/getflags/flags2_asyncio_executor.py @@ -22,23 +22,23 @@ MAX_CONCUR_REQ = 1000 -async def get_flag(session: httpx.AsyncClient, # <2> +async def get_flag(client: httpx.AsyncClient, # <2> base_url: str, cc: str) -> bytes: url = f'{base_url}/{cc}/{cc}.gif'.lower() - resp = await session.get(url, timeout=3.1, follow_redirects=True) # <3> + resp = await client.get(url, timeout=3.1, follow_redirects=True) # <3> resp.raise_for_status() return resp.content -async def download_one(session: httpx.AsyncClient, +async def download_one(client: httpx.AsyncClient, cc: str, base_url: str, semaphore: asyncio.Semaphore, verbose: bool) -> DownloadStatus: try: async with semaphore: - image = await get_flag(session, base_url, cc) + image = await get_flag(client, base_url, cc) except httpx.HTTPStatusError as exc: res = exc.response if res.status_code == HTTPStatus.NOT_FOUND: @@ -64,8 +64,8 @@ async def supervisor(cc_list: list[str], concur_req: int) -> Counter[DownloadStatus]: # <1> counter: Counter[DownloadStatus] = Counter() semaphore = asyncio.Semaphore(concur_req) # <2> - async with httpx.AsyncClient() as session: - to_do = [download_one(session, cc, base_url, semaphore, verbose) + async with httpx.AsyncClient() as client: + to_do = [download_one(client, cc, base_url, semaphore, verbose) for cc in sorted(cc_list)] # <3> to_do_iter = asyncio.as_completed(to_do) # <4> if not verbose: diff --git a/20-futures/getflags/flags2_common.py b/20-executors/getflags/flags2_common.py similarity index 100% rename from 20-futures/getflags/flags2_common.py rename to 20-executors/getflags/flags2_common.py diff --git a/20-futures/getflags/flags2_sequential.py b/20-executors/getflags/flags2_sequential.py similarity index 100% rename from 20-futures/getflags/flags2_sequential.py rename to 20-executors/getflags/flags2_sequential.py diff --git a/20-futures/getflags/flags2_threadpool.py b/20-executors/getflags/flags2_threadpool.py similarity index 87% rename from 20-futures/getflags/flags2_threadpool.py rename to 20-executors/getflags/flags2_threadpool.py index 8955c7c..62f6c3b 100755 --- a/20-futures/getflags/flags2_threadpool.py +++ b/20-executors/getflags/flags2_threadpool.py @@ -20,7 +20,7 @@ # tag::FLAGS2_THREADPOOL[] from collections import Counter -from concurrent import futures +from concurrent.futures import ThreadPoolExecutor, as_completed import httpx import tqdm # type: ignore @@ -37,13 +37,13 @@ def download_many(cc_list: list[str], verbose: bool, concur_req: int) -> Counter[DownloadStatus]: counter: Counter[DownloadStatus] = Counter() - with futures.ThreadPoolExecutor(max_workers=concur_req) as executor: # <4> + with ThreadPoolExecutor(max_workers=concur_req) as executor: # <4> to_do_map = {} # <5> for cc in sorted(cc_list): # <6> future = executor.submit(download_one, cc, base_url, verbose) # <7> to_do_map[future] = cc # <8> - done_iter = futures.as_completed(to_do_map) # <9> + done_iter = as_completed(to_do_map) # <9> if not verbose: done_iter = tqdm.tqdm(done_iter, total=len(cc_list)) # <10> for future in done_iter: # <11> @@ -52,7 +52,7 @@ def download_many(cc_list: list[str], except httpx.HTTPStatusError as exc: # <13> error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' error_msg = error_msg.format(resp=exc.response) - except httpx.RequestError as exc: # <15> + except httpx.RequestError as exc: error_msg = f'{exc} {type(exc)}'.strip() except KeyboardInterrupt: break @@ -63,7 +63,7 @@ def download_many(cc_list: list[str], status = DownloadStatus.ERROR counter[status] += 1 if verbose and error_msg: - cc = to_do_map[future] # <16> + cc = to_do_map[future] # <14> print(f'{cc} error: {error_msg}') return counter diff --git a/20-executors/getflags/flags3_asyncio.py b/20-executors/getflags/flags3_asyncio.py new file mode 100755 index 0000000..9554dd2 --- /dev/null +++ b/20-executors/getflags/flags3_asyncio.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 + +"""Download flags of countries (with error handling). + +asyncio async/await version + +""" +# tag::FLAGS2_ASYNCIO_TOP[] +import asyncio +from collections import Counter +from http import HTTPStatus +from pathlib import Path + +import httpx +import tqdm # type: ignore + +from flags2_common import main, DownloadStatus, save_flag + +# low concurrency default to avoid errors from remote site, +# such as 503 - Service Temporarily Unavailable +DEFAULT_CONCUR_REQ = 5 +MAX_CONCUR_REQ = 1000 + +async def get_flag(client: httpx.AsyncClient, # <1> + base_url: str, + cc: str) -> bytes: + url = f'{base_url}/{cc}/{cc}.gif'.lower() + resp = await client.get(url, timeout=3.1, follow_redirects=True) # <2> + resp.raise_for_status() + return resp.content + +# tag::FLAGS3_ASYNCIO_GET_COUNTRY[] +async def get_country(client: httpx.AsyncClient, + base_url: str, + cc: str) -> str: # <1> + url = f'{base_url}/{cc}/metadata.json'.lower() + resp = await client.get(url, timeout=3.1, follow_redirects=True) + resp.raise_for_status() + metadata = resp.json() # <2> + return metadata['country'] # <3> +# end::FLAGS3_ASYNCIO_GET_COUNTRY[] + +# tag::FLAGS3_ASYNCIO_DOWNLOAD_ONE[] +async def download_one(client: httpx.AsyncClient, + cc: str, + base_url: str, + semaphore: asyncio.Semaphore, + verbose: bool) -> DownloadStatus: + try: + async with semaphore: # <1> + image = await get_flag(client, base_url, cc) + async with semaphore: # <2> + country = await get_country(client, base_url, cc) + except httpx.HTTPStatusError as exc: + res = exc.response + if res.status_code == HTTPStatus.NOT_FOUND: + status = DownloadStatus.NOT_FOUND + msg = f'not found: {res.url}' + else: + raise + else: + filename = country.replace(' ', '_') # <3> + await asyncio.to_thread(save_flag, image, f'{filename}.gif') + status = DownloadStatus.OK + msg = 'OK' + if verbose and msg: + print(cc, msg) + return status +# end::FLAGS3_ASYNCIO_DOWNLOAD_ONE[] + +# tag::FLAGS2_ASYNCIO_START[] +async def supervisor(cc_list: list[str], + base_url: str, + verbose: bool, + concur_req: int) -> Counter[DownloadStatus]: # <1> + counter: Counter[DownloadStatus] = Counter() + semaphore = asyncio.Semaphore(concur_req) # <2> + async with httpx.AsyncClient() as client: + to_do = [download_one(client, cc, base_url, semaphore, verbose) + for cc in sorted(cc_list)] # <3> + to_do_iter = asyncio.as_completed(to_do) # <4> + if not verbose: + to_do_iter = tqdm.tqdm(to_do_iter, total=len(cc_list)) # <5> + error: httpx.HTTPError | None = None # <6> + for coro in to_do_iter: # <7> + try: + status = await coro # <8> + except httpx.HTTPStatusError as exc: + error_msg = 'HTTP error {resp.status_code} - {resp.reason_phrase}' + error_msg = error_msg.format(resp=exc.response) + error = exc # <9> + except httpx.RequestError as exc: + error_msg = f'{exc} {type(exc)}'.strip() + error = exc # <10> + except KeyboardInterrupt: + break + + if error: + status = DownloadStatus.ERROR # <11> + if verbose: + url = str(error.request.url) # <12> + cc = Path(url).stem.upper() # <13> + print(f'{cc} error: {error_msg}') + counter[status] += 1 + + return counter + +def download_many(cc_list: list[str], + base_url: str, + verbose: bool, + concur_req: int) -> Counter[DownloadStatus]: + coro = supervisor(cc_list, base_url, verbose, concur_req) + counts = asyncio.run(coro) # <14> + + return counts + +if __name__ == '__main__': + main(download_many, DEFAULT_CONCUR_REQ, MAX_CONCUR_REQ) +# end::FLAGS2_ASYNCIO_START[] diff --git a/20-futures/getflags/flags_asyncio.py b/20-executors/getflags/flags_asyncio.py similarity index 77% rename from 20-futures/getflags/flags_asyncio.py rename to 20-executors/getflags/flags_asyncio.py index ba28065..588d166 100755 --- a/20-futures/getflags/flags_asyncio.py +++ b/20-executors/getflags/flags_asyncio.py @@ -17,15 +17,15 @@ from flags import BASE_URL, save_flag, main # <2> -async def download_one(session: AsyncClient, cc: str): # <3> - image = await get_flag(session, cc) +async def download_one(client: AsyncClient, cc: str): # <3> + image = await get_flag(client, cc) save_flag(image, f'{cc}.gif') print(cc, end=' ', flush=True) return cc -async def get_flag(session: AsyncClient, cc: str) -> bytes: # <4> +async def get_flag(client: AsyncClient, cc: str) -> bytes: # <4> url = f'{BASE_URL}/{cc}/{cc}.gif'.lower() - resp = await session.get(url, timeout=6.1, + resp = await client.get(url, timeout=6.1, follow_redirects=True) # <5> return resp.read() # <6> # end::FLAGS_ASYNCIO_TOP[] @@ -35,8 +35,8 @@ def download_many(cc_list: list[str]) -> int: # <1> return asyncio.run(supervisor(cc_list)) # <2> async def supervisor(cc_list: list[str]) -> int: - async with AsyncClient() as session: # <3> - to_do = [download_one(session, cc) + async with AsyncClient() as client: # <3> + to_do = [download_one(client, cc) for cc in sorted(cc_list)] # <4> res = await asyncio.gather(*to_do) # <5> diff --git a/20-futures/getflags/flags_threadpool.py b/20-executors/getflags/flags_threadpool.py similarity index 100% rename from 20-futures/getflags/flags_threadpool.py rename to 20-executors/getflags/flags_threadpool.py diff --git a/20-futures/getflags/flags_threadpool_futures.py b/20-executors/getflags/flags_threadpool_futures.py similarity index 100% rename from 20-futures/getflags/flags_threadpool_futures.py rename to 20-executors/getflags/flags_threadpool_futures.py diff --git a/20-executors/getflags/httpx-error-tree/drawtree.py b/20-executors/getflags/httpx-error-tree/drawtree.py new file mode 100755 index 0000000..f2d9fea --- /dev/null +++ b/20-executors/getflags/httpx-error-tree/drawtree.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +from tree import tree + + +SP = '\N{SPACE}' +HLIN = '\N{BOX DRAWINGS LIGHT HORIZONTAL}' # ─ +ELBOW = f'\N{BOX DRAWINGS LIGHT UP AND RIGHT}{HLIN*2}{SP}' # └── +TEE = f'\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}{HLIN*2}{SP}' # ├── +PIPE = f'\N{BOX DRAWINGS LIGHT VERTICAL}{SP*3}' # │ + + +def cls_name(cls): + module = 'builtins.' if cls.__module__ == 'builtins' else '' + return module + cls.__name__ + +def render_lines(tree_iter): + cls, _, _ = next(tree_iter) + yield cls_name(cls) + prefix = '' + + for cls, level, last in tree_iter: + prefix = prefix[:4 * (level-1)] + prefix = prefix.replace(TEE, PIPE).replace(ELBOW, SP*4) + prefix += ELBOW if last else TEE + yield prefix + cls_name(cls) + + +def draw(cls): + for line in render_lines(tree(cls)): + print(line) + + +if __name__ == '__main__': + draw(Exception) diff --git a/20-executors/getflags/httpx-error-tree/tree.py b/20-executors/getflags/httpx-error-tree/tree.py new file mode 100755 index 0000000..950bbfe --- /dev/null +++ b/20-executors/getflags/httpx-error-tree/tree.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import httpx # make httpx classes available to .__subclasses__() + + +def tree(cls, level=0, last_sibling=True): + yield cls, level, last_sibling + + # get RuntimeError and exceptions defined in httpx + subclasses = [sub for sub in cls.__subclasses__() + if sub is RuntimeError or sub.__module__ == 'httpx'] + if subclasses: + last = subclasses[-1] + for sub in subclasses: + yield from tree(sub, level+1, sub is last) + + +def display(cls): + for cls, level, _ in tree(cls): + indent = ' ' * 4 * level + module = 'builtins.' if cls.__module__ == 'builtins' else '' + print(f'{indent}{module}{cls.__name__}') + + +if __name__ == '__main__': + display(Exception) diff --git a/20-futures/getflags/requirements.txt b/20-executors/getflags/requirements.txt similarity index 65% rename from 20-futures/getflags/requirements.txt rename to 20-executors/getflags/requirements.txt index b8bb630..9b1faeb 100644 --- a/20-futures/getflags/requirements.txt +++ b/20-executors/getflags/requirements.txt @@ -1,10 +1,10 @@ anyio==3.3.2 -certifi==2021.5.30 +certifi==2024.7.4 charset-normalizer==2.0.6 -h11==0.12.0 +h11==0.16.0 httpcore==0.13.7 httpx==1.0.0b0 -idna==3.2 +idna==3.7 rfc3986==1.5.0 sniffio==1.2.0 -tqdm==4.62.3 +tqdm==4.66.3 diff --git a/20-futures/getflags/slow_server.py b/20-executors/getflags/slow_server.py similarity index 100% rename from 20-futures/getflags/slow_server.py rename to 20-executors/getflags/slow_server.py diff --git a/20-futures/primes/primes.py b/20-executors/primes/primes.py similarity index 100% rename from 20-futures/primes/primes.py rename to 20-executors/primes/primes.py diff --git a/20-futures/primes/proc_pool.py b/20-executors/primes/proc_pool.py similarity index 100% rename from 20-futures/primes/proc_pool.py rename to 20-executors/primes/proc_pool.py diff --git a/20-futures/getflags/.gitignore b/20-futures/getflags/.gitignore deleted file mode 100644 index 8484300..0000000 --- a/20-futures/getflags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flags/ \ No newline at end of file diff --git a/20-futures/getflags/tree.py b/20-futures/getflags/tree.py deleted file mode 100644 index 4f65697..0000000 --- a/20-futures/getflags/tree.py +++ /dev/null @@ -1,38 +0,0 @@ -import httpx - -def tree(cls, level=0): - yield cls.__name__, level - for sub_cls in cls.__subclasses__(): - yield from tree(sub_cls, level+1) - - -def display(cls): - for cls_name, level in tree(cls): - indent = ' ' * 4 * level - print(f'{indent}{cls_name}') - - -def find_roots(module): - exceptions = [] - for name in dir(module): - obj = getattr(module, name) - if isinstance(obj, type) and issubclass(obj, BaseException): - exceptions.append(obj) - roots = [] - for exc in exceptions: - root = True - for other in exceptions: - if exc is not other and issubclass(exc, other): - root = False - break - if root: - roots.append(exc) - return roots - - -def main(): - for exc in find_roots(httpx): - display(exc) - -if __name__ == '__main__': - main() diff --git a/21-async/mojifinder/requirements.txt b/21-async/mojifinder/requirements.txt index 1f7c3d6..6831fee 100644 --- a/21-async/mojifinder/requirements.txt +++ b/21-async/mojifinder/requirements.txt @@ -1,7 +1,7 @@ click==7.1.2 fastapi==0.65.2 -h11==0.12.0 -pydantic==1.8.2 -starlette==0.13.6 +h11==0.16.0 +pydantic==1.10.13 +starlette==0.40.0 typing-extensions==3.7.4.3 uvicorn==0.13.4 diff --git a/21-async/mojifinder/tcp_mojifinder.py b/21-async/mojifinder/tcp_mojifinder.py index 8ff4e50..4da7188 100755 --- a/21-async/mojifinder/tcp_mojifinder.py +++ b/21-async/mojifinder/tcp_mojifinder.py @@ -14,26 +14,28 @@ async def finder(index: InvertedIndex, # <2> reader: asyncio.StreamReader, - writer: asyncio.StreamWriter): + writer: asyncio.StreamWriter) -> None: client = writer.get_extra_info('peername') # <3> while True: # <4> writer.write(PROMPT) # can't await! # <5> await writer.drain() # must await! # <6> data = await reader.readline() # <7> + if not data: # <8> + break try: - query = data.decode().strip() # <8> - except UnicodeDecodeError: # <9> + query = data.decode().strip() # <9> + except UnicodeDecodeError: # <10> query = '\x00' - print(f' From {client}: {query!r}') # <10> + print(f' From {client}: {query!r}') # <11> if query: - if ord(query[:1]) < 32: # <11> + if ord(query[:1]) < 32: # <12> break - results = await search(query, index, writer) # <12> - print(f' To {client}: {results} results.') # <13> + results = await search(query, index, writer) # <13> + print(f' To {client}: {results} results.') # <14> - writer.close() # <14> - await writer.wait_closed() # <15> - print(f'Close {client}.') # <16> + writer.close() # <15> + await writer.wait_closed() # <16> + print(f'Close {client}.') # <17> # end::TCP_MOJIFINDER_TOP[] # tag::TCP_MOJIFINDER_SEARCH[] @@ -52,7 +54,7 @@ async def search(query: str, # <1> # end::TCP_MOJIFINDER_SEARCH[] # tag::TCP_MOJIFINDER_MAIN[] -async def supervisor(index: InvertedIndex, host: str, port: int): +async def supervisor(index: InvertedIndex, host: str, port: int) -> None: server = await asyncio.start_server( # <1> functools.partial(finder, index), # <2> host, port) # <3> diff --git a/21-async/mojifinder/web_mojifinder.py b/21-async/mojifinder/web_mojifinder.py index 7e0ff96..5f0cf5c 100644 --- a/21-async/mojifinder/web_mojifinder.py +++ b/21-async/mojifinder/web_mojifinder.py @@ -7,25 +7,26 @@ from charindex import InvertedIndex -app = FastAPI( # <1> +STATIC_PATH = Path(__file__).parent.absolute() / 'static' # <1> + +app = FastAPI( # <2> title='Mojifinder Web', description='Search for Unicode characters by name.', ) -class CharName(BaseModel): # <2> +class CharName(BaseModel): # <3> char: str name: str -def init(app): # <3> +def init(app): # <4> app.state.index = InvertedIndex() - static = Path(__file__).parent.absolute() / 'static' # <4> - app.state.form = (static / 'form.html').read_text() + app.state.form = (STATIC_PATH / 'form.html').read_text() init(app) # <5> @app.get('/search', response_model=list[CharName]) # <6> async def search(q: str): # <7> - chars = app.state.index.search(q) + chars = sorted(app.state.index.search(q)) return ({'char': c, 'name': name(c)} for c in chars) # <8> @app.get('/', response_class=HTMLResponse, include_in_schema=False) diff --git a/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py b/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py index e34a7c3..b2cffab 100644 --- a/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py +++ b/22-dyn-attr-prop/bulkfood/bulkfood_v2prop.py @@ -30,8 +30,8 @@ >>> nutmeg = LineItem('Moluccan nutmeg', 8, 13.95) >>> nutmeg.weight, nutmeg.price # <1> (8, 13.95) - >>> sorted(vars(nutmeg).items()) # <2> - [('description', 'Moluccan nutmeg'), ('price', 13.95), ('weight', 8)] + >>> nutmeg.__dict__ # <2> + {'description': 'Moluccan nutmeg', 'weight': 8, 'price': 13.95} # end::LINEITEM_V2_PROP_DEMO[] diff --git a/22-dyn-attr-prop/oscon/explore0.py b/22-dyn-attr-prop/oscon/explore0.py index 1ea7843..c881d16 100644 --- a/22-dyn-attr-prop/oscon/explore0.py +++ b/22-dyn-attr-prop/oscon/explore0.py @@ -54,12 +54,15 @@ def __getattr__(self, name): # <2> except AttributeError: return FrozenJSON.build(self.__data[name]) # <4> + def __dir__(self): # <5> + return self.__data.keys() + @classmethod - def build(cls, obj): # <5> - if isinstance(obj, abc.Mapping): # <6> + def build(cls, obj): # <6> + if isinstance(obj, abc.Mapping): # <7> return cls(obj) - elif isinstance(obj, abc.MutableSequence): # <7> + elif isinstance(obj, abc.MutableSequence): # <8> return [cls.build(item) for item in obj] - else: # <8> + else: # <9> return obj # end::EXPLORE0[] diff --git a/22-dyn-attr-prop/oscon/explore1.py b/22-dyn-attr-prop/oscon/explore1.py index ecb53b0..6942079 100644 --- a/22-dyn-attr-prop/oscon/explore1.py +++ b/22-dyn-attr-prop/oscon/explore1.py @@ -62,11 +62,14 @@ def __init__(self, mapping): # end::EXPLORE1[] def __getattr__(self, name): - if hasattr(self.__data, name): + try: return getattr(self.__data, name) - else: + except AttributeError: return FrozenJSON.build(self.__data[name]) + def __dir__(self): # <5> + return self.__data.keys() + @classmethod def build(cls, obj): if isinstance(obj, abc.Mapping): diff --git a/22-dyn-attr-prop/oscon/explore2.py b/22-dyn-attr-prop/oscon/explore2.py index 8cb9348..b50d49f 100644 --- a/22-dyn-attr-prop/oscon/explore2.py +++ b/22-dyn-attr-prop/oscon/explore2.py @@ -47,8 +47,11 @@ def __init__(self, mapping): self.__data[key] = value def __getattr__(self, name): - if hasattr(self.__data, name): + try: return getattr(self.__data, name) - else: + except AttributeError: return FrozenJSON(self.__data[name]) # <4> + + def __dir__(self): + return self.__data.keys() # end::EXPLORE2[] diff --git a/22-dyn-attr-prop/oscon/schedule_v1.py b/22-dyn-attr-prop/oscon/schedule_v1.py index 1be6c41..12aab71 100644 --- a/22-dyn-attr-prop/oscon/schedule_v1.py +++ b/22-dyn-attr-prop/oscon/schedule_v1.py @@ -23,8 +23,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) # <1> def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' # <2> + return f'<{self.__class__.__name__} serial={self.serial!r}>' # <2> def load(path=JSON_PATH): records = {} # <3> diff --git a/22-dyn-attr-prop/oscon/schedule_v2.py b/22-dyn-attr-prop/oscon/schedule_v2.py index 35827eb..5f264f7 100644 --- a/22-dyn-attr-prop/oscon/schedule_v2.py +++ b/22-dyn-attr-prop/oscon/schedule_v2.py @@ -29,8 +29,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod # <3> def fetch(key): @@ -44,10 +43,9 @@ def fetch(key): class Event(Record): # <1> def __repr__(self): - if hasattr(self, 'name'): # <2> - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: + try: + return f'<{self.__class__.__name__} {self.name!r}>' # <2> + except AttributeError: return super().__repr__() @property diff --git a/22-dyn-attr-prop/oscon/schedule_v3.py b/22-dyn-attr-prop/oscon/schedule_v3.py index 786a412..7683b2d 100644 --- a/22-dyn-attr-prop/oscon/schedule_v3.py +++ b/22-dyn-attr-prop/oscon/schedule_v3.py @@ -33,8 +33,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -46,11 +45,10 @@ def fetch(key): class Event(Record): def __repr__(self): - if hasattr(self, 'name'): # <3> - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: - return super().__repr__() # <4> + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: + return super().__repr__() @property def venue(self): diff --git a/22-dyn-attr-prop/oscon/schedule_v4.py b/22-dyn-attr-prop/oscon/schedule_v4.py index a609917..5b968e1 100644 --- a/22-dyn-attr-prop/oscon/schedule_v4.py +++ b/22-dyn-attr-prop/oscon/schedule_v4.py @@ -32,8 +32,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -52,11 +51,10 @@ def __init__(self, **kwargs): # end::SCHEDULE4_INIT[] def __repr__(self): - if hasattr(self, 'name'): - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: - return super().__repr__() # <4> + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: + return super().__repr__() @property def venue(self): diff --git a/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py b/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py index ac5d5de..abc72bf 100644 --- a/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py +++ b/22-dyn-attr-prop/oscon/schedule_v4_hasattr.py @@ -31,8 +31,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -44,11 +43,10 @@ def fetch(key): class Event(Record): def __repr__(self): - if hasattr(self, 'name'): - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: - return super().__repr__() # <4> + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: + return super().__repr__() @property def venue(self): diff --git a/22-dyn-attr-prop/oscon/schedule_v5.py b/22-dyn-attr-prop/oscon/schedule_v5.py index 2517e57..7a9543b 100644 --- a/22-dyn-attr-prop/oscon/schedule_v5.py +++ b/22-dyn-attr-prop/oscon/schedule_v5.py @@ -36,8 +36,7 @@ def __init__(self, **kwargs): self.__dict__.update(kwargs) def __repr__(self): - cls_name = self.__class__.__name__ - return f'<{cls_name} serial={self.serial!r}>' + return f'<{self.__class__.__name__} serial={self.serial!r}>' @staticmethod def fetch(key): @@ -49,10 +48,9 @@ def fetch(key): class Event(Record): def __repr__(self): - if hasattr(self, 'name'): - cls_name = self.__class__.__name__ - return f'<{cls_name} {self.name!r}>' - else: + try: + return f'<{self.__class__.__name__} {self.name!r}>' + except AttributeError: return super().__repr__() # tag::SCHEDULE5_CACHED_PROPERTY[] diff --git a/23-descriptor/bulkfood/model_v5.py b/23-descriptor/bulkfood/model_v5.py index 018afcd..4c35698 100644 --- a/23-descriptor/bulkfood/model_v5.py +++ b/23-descriptor/bulkfood/model_v5.py @@ -30,7 +30,7 @@ class NonBlank(Validated): def validate(self, name, value): value = value.strip() - if len(value) == 0: + if not value: # <2> raise ValueError(f'{name} cannot be blank') - return value # <2> + return value # <3> # end::MODEL_V5_VALIDATED_SUB[] diff --git a/23-descriptor/descriptorkinds.py b/23-descriptor/descriptorkinds.py index 44f4018..13df709 100644 --- a/23-descriptor/descriptorkinds.py +++ b/23-descriptor/descriptorkinds.py @@ -5,21 +5,18 @@ >>> obj = Managed() # <1> >>> obj.over # <2> - -> Overriding.__get__(, , - ) + -> Overriding.__get__(, , ) >>> Managed.over # <3> -> Overriding.__get__(, None, ) >>> obj.over = 7 # <4> -> Overriding.__set__(, , 7) >>> obj.over # <5> - -> Overriding.__get__(, , - ) + -> Overriding.__get__(, , ) >>> obj.__dict__['over'] = 8 # <6> >>> vars(obj) # <7> {'over': 8} >>> obj.over # <8> - -> Overriding.__get__(, , - ) + -> Overriding.__get__(, , ) # end::DESCR_KINDS_DEMO1[] @@ -50,8 +47,7 @@ >>> obj = Managed() >>> obj.non_over # <1> - -> NonOverriding.__get__(, , - ) + -> NonOverriding.__get__(, , ) >>> obj.non_over = 7 # <2> >>> obj.non_over # <3> 7 @@ -59,8 +55,7 @@ -> NonOverriding.__get__(, None, ) >>> del obj.non_over # <5> >>> obj.non_over # <6> - -> NonOverriding.__get__(, , - ) + -> NonOverriding.__get__(, , ) # end::DESCR_KINDS_DEMO3[] @@ -88,7 +83,7 @@ >>> Managed.spam() Traceback (most recent call last): ... - TypeError: spam() missing 1 required positional argument: 'self' + TypeError: Managed.spam() missing 1 required positional argument: 'self' >>> Managed.spam(obj) -> Managed.spam() >>> Managed.spam.__get__(obj) # doctest: +ELLIPSIS @@ -156,15 +151,15 @@ def cls_name(obj_or_cls): def display(obj): cls = type(obj) if cls is type: - return ''.format(obj.__name__) + return f'' elif cls in [type(None), int]: return repr(obj) else: - return '<{} object>'.format(cls_name(obj)) + return f'<{cls_name(obj)} object>' def print_args(name, *args): pseudo_args = ', '.join(display(x) for x in args) - print('-> {}.__{}__({})'.format(cls_name(args[0]), name, pseudo_args)) + print(f'-> {cls_name(args[0])}.__{name}__({pseudo_args})') ### essential classes for this example ### @@ -199,6 +194,6 @@ class Managed: # <5> non_over = NonOverriding() def spam(self): # <6> - print('-> Managed.spam({})'.format(display(self))) + print(f'-> Managed.spam({display(self)})') # end::DESCR_KINDS[] diff --git a/24-class-metaprog/checked/metaclass/checkedlib.py b/24-class-metaprog/checked/metaclass/checkedlib.py index 8207dbe..768a0f6 100644 --- a/24-class-metaprog/checked/metaclass/checkedlib.py +++ b/24-class-metaprog/checked/metaclass/checkedlib.py @@ -78,7 +78,9 @@ def __init__(self, name: str, constructor: Callable) -> None: self.storage_name = '_' + name # <1> self.constructor = constructor - def __get__(self, instance, owner=None): # <2> + def __get__(self, instance, owner=None): + if instance is None: # <2> + return self return getattr(instance, self.storage_name) # <3> def __set__(self, instance: Any, value: Any) -> None: diff --git a/24-class-metaprog/factories.py b/24-class-metaprog/factories.py index 38ec763..cf61d89 100644 --- a/24-class-metaprog/factories.py +++ b/24-class-metaprog/factories.py @@ -49,9 +49,8 @@ def __iter__(self) -> Iterator[Any]: # <5> yield getattr(self, name) def __repr__(self): # <6> - values = ', '.join( - '{}={!r}'.format(*i) for i in zip(self.__slots__, self) - ) + values = ', '.join(f'{name}={value!r}' + for name, value in zip(self.__slots__, self)) cls_name = self.__class__.__name__ return f'{cls_name}({values})' diff --git a/24-class-metaprog/metabunch/from3.6/bunch.py b/24-class-metaprog/metabunch/from3.6/bunch.py index a8b3d5a..aa0a65e 100644 --- a/24-class-metaprog/metabunch/from3.6/bunch.py +++ b/24-class-metaprog/metabunch/from3.6/bunch.py @@ -28,7 +28,7 @@ >>> Point(x=1, y=2, z=3) Traceback (most recent call last): ... - AttributeError: 'Point' object has no attribute 'z' + AttributeError: No slots left for: 'z' >>> p = Point(x=21) >>> p.y = 42 >>> p @@ -51,7 +51,8 @@ def __init__(self, **kwargs): # <4> for name, default in defaults.items(): # <5> setattr(self, name, kwargs.pop(name, default)) if kwargs: # <6> - setattr(self, *kwargs.popitem()) + extra = ', '.join(kwargs) + raise AttributeError(f'No slots left for: {extra!r}') def __repr__(self): # <7> rep = ', '.join(f'{name}={value!r}' diff --git a/24-class-metaprog/metabunch/from3.6/bunch_test.py b/24-class-metaprog/metabunch/from3.6/bunch_test.py index 322487a..166e600 100644 --- a/24-class-metaprog/metabunch/from3.6/bunch_test.py +++ b/24-class-metaprog/metabunch/from3.6/bunch_test.py @@ -26,7 +26,7 @@ def test_init(): def test_init_wrong_argument(): with pytest.raises(AttributeError) as exc: p = Point(x=1.2, y=3.4, flavor='coffee') - assert "no attribute 'flavor'" in str(exc.value) + assert 'flavor' in str(exc.value) def test_slots(): diff --git a/24-class-metaprog/timeslice.py b/24-class-metaprog/timeslice.py new file mode 100644 index 0000000..5a52457 --- /dev/null +++ b/24-class-metaprog/timeslice.py @@ -0,0 +1,91 @@ + +""" +Could this be valid Python? + + if now >= T[4:20:PM]: chill() + + +>>> t = T[4:20] +>>> t +T[4:20] +>>> h, m, s = t +>>> h, m, s +(4, 20, 0) +>>> t[11:59:AM] +T[11:59:AM] +>>> start = t[9:O1:PM] +>>> start +T[9:O1:PM] +>>> start.h, start.m, start.s, start.pm +(9, 1, 0, True) +>>> now = T[7:O1:PM] +>>> T[4:OO:PM] +T[4:OO:PM] +>>> now > T[4:20:PM] +True +""" + +import functools + +AM = -2 +PM = -1 + +for n in range(10): + globals()[f'O{n}'] = n +OO = 0 + +@functools.total_ordering +class T(): + + def __init__(self, arg): + if isinstance(arg, slice): + h = arg.start or 0 + m = arg.stop or 0 + s = arg.step or 0 + else: + h, m, s = 0, 0, arg + if m in (AM, PM): + self.pm = m == PM + m = 0 + elif s in (AM, PM): + self.pm = s == PM + s = 0 + else: + self.pm = None + self.h, self.m, self.s = h, m, s + + def __class_getitem__(cls, arg): + return cls(arg) + + def __getitem__(self, arg): + return(type(self)(arg)) + + def __repr__(self): + h, m, s = self.h, self.m, self.s or None + if m == 0: + m = f'OO' + elif m < 10: + m = f'O{m}' + s = '' if s is None else s + if self.pm is None: + pm = '' + else: + pm = ':' + ('AM', 'PM')[self.pm] + return f'T[{h}:{m}{s}{pm}]' + + def __iter__(self): + yield from (self.h, self.m, self.s) + + def __eq__(self, other): + return tuple(self) == tuple(other) + + def __lt__(self, other): + return tuple(self) < tuple(other) + + def __add__(self, other): + """ + >>> T[11:O5:AM] + 15 # TODO: preserve pm field + T[11:20] + """ + if isinstance(other, int): + return self[self.h:self.m + other:self.pm] diff --git a/README.md b/README.md index f0854e9..234c8da 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,7 @@ # Fluent Python 2e example code -Example code for the book **Fluent Python, 2nd edition** by Luciano Ramalho (O'Reilly, 2021). +Example code for the book **Fluent Python, Second Edition** by Luciano Ramalho (O'Reilly, 2022). -> **BEWARE**: This is a work in progress! -> -> * Code here may change and disappear without warning. -> -> * Major reorganizations may happen at any time. -> -> * No promises. No guarantees. Use at own risk. ## Table of Contents @@ -16,37 +9,37 @@ All chapters are undergoing review and updates, including significant rewrites i New chapters in **Fluent Python 2e** are marked with 🆕. -🚨 This table of contents is subject to change at any time until the book goes to the printer. +> 🚨  This table of contents is subject to change at any time until the book goes to the printer.
+Latest change: Old **Part I—Prologue** merged into new **Part I—Data Structures**; parts renumbered accordingly; chapter numbers unchanged. Part / Chapter #|Title|Directory|1st ed. Chapter # ---:|---|---|:---: -**I – Prologue**| +**I – Data Structures**| 1|The Python Data Model|[01-data-model](01-data-model)|1 -**II – Data Structures**| 2|An Array of Sequences|[02-array-seq](02-array-seq)|2 3|Dictionaries and Sets|[03-dict-set](03-dict-set)|3 4|Unicode Text versus Bytes|[04-text-byte](04-text-byte)|4 5|Data Class Builders|[05-data-classes](05-data-classes)|🆕 6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)|8 -**III – Functions as Objects**| +**II – Functions as Objects**| 7|Funcions as First-Class Objects|[07-1class-func](07-1class-func)|5 -8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)|🆕 -9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)|7 +8|Type Hints in Functions|[08-def-type-hints](08-def-type-hints)|🆕 +9|Decorators and Closures|[09-closure-deco](09-closure-deco)|7 10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)|6 -**IV – Object-Oriented Idioms**| +**III – Object-Oriented Idioms**| 11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)|9 -12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)|10 +12|Special Methods for Sequences|[12-seq-hacking](12-seq-hacking)|10 13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)|11 -14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)|12 +14|Inheritance: For Better or For Worse|[14-inheritance](14-inheritance)|12 15|More About Type Hints|[15-more-types](15-more-types)|🆕 -16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)|13 -**V – Control Flow**| +16|Operator Overloading|[16-op-overloading](16-op-overloading)|13 +**IV – Control Flow**| 17|Iterators, Generators, and Classic Coroutines|[17-it-generator](17-it-generator)|14 -18|Context Managers and else Blocks|[18-with-match](18-with-match)|15 +18|with, match, and else Blocks|[18-with-match](18-with-match)|15 19|Concurrency Models in Python|[19-concurrency](19-concurrency)|🆕 -20|Concurrency with Futures|[20-futures](20-futures)|17 +20|Concurrent Executors|[20-executors](20-executors)|17 21|Asynchronous Programming|[21-async](21-async)|18 -**VI – Metaprogramming**| +**V – Metaprogramming**| 22|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)|19 23|Attribute Descriptors|[23-descriptor](23-descriptor)|20 24|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)|21 diff --git a/links/FPY.LI.htaccess b/links/FPY.LI.htaccess new file mode 100644 index 0000000..5607fa4 --- /dev/null +++ b/links/FPY.LI.htaccess @@ -0,0 +1,1089 @@ +# to recreate or update this file: +# $ cat custom.htaccess short.htaccess > FPY.LI.htaccess + +ErrorDocument 404 /404.html + +# main resources +RedirectTemp /book https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /code https://github.com/fluentpython/example-code-2e +RedirectTemp /home https://www.fluentpython.com/ + +# URLs mentioned at least three times +RedirectTemp /bisect https://www.fluentpython.com/extra/ordered-sequences-with-bisect/ +RedirectTemp /cardxvi https://www.python.org/dev/peps/pep-0484/#the-numeric-tower +RedirectTemp /collec https://docs.python.org/3/library/collections.html +RedirectTemp /dask https://dask.org/ +RedirectTemp /dtmodel https://docs.python.org/3/reference/datamodel.html +RedirectTemp /descr101 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /descrhow https://docs.python.org/3/howto/descriptor.html +RedirectTemp /doctest https://docs.python.org/3/library/doctest.html +RedirectTemp /effectpy https://effectivepython.com/ +RedirectTemp /fmtspec https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /gunicorn https://gunicorn.org/ +RedirectTemp /hashint https://www.fluentpython.com/extra/internals-of-sets-and-dicts/ +RedirectTemp /hattingh https://www.oreilly.com/library/view/using-asyncio-in/9781492075325/ +RedirectTemp /httpx https://www.python-httpx.org/ +RedirectTemp /initvar https://docs.python.org/3/library/dataclasses.html#init-only-variables +RedirectTemp /mypy https://mypy.readthedocs.io/en/stable/ +RedirectTemp /norvigdp http://norvig.com/design-patterns/ +RedirectTemp /nsphere https://en.wikipedia.org/wiki/N-sphere +RedirectTemp /oldcoro https://www.fluentpython.com/extra/classic-coroutines/ +RedirectTemp /pandas https://pandas.pydata.org/ +RedirectTemp /pycook3 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ +RedirectTemp /pynut3 https://www.oreilly.com/library/view/python-in-a/9781491913833/ +RedirectTemp /pypydif https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /shed4051 https://github.com/python/typeshed/issues/4051 +RedirectTemp /specattr https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /typecoro https://docs.python.org/3.10/library/typing.html#typing.Coroutine +RedirectTemp /typing https://docs.python.org/3/library/typing.html +RedirectTemp /weakref https://www.fluentpython.com/extra/weak-references/ + +# URL added during QA of the Second Edition +RedirectTemp /bdfl https://www.artima.com/weblogs/viewpost.jsp?thread=235725 + +# Python Enhancement Proposals +RedirectTemp /pep218 https://www.python.org/dev/peps/pep-0218/ +RedirectTemp /pep227 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /pep255 https://www.python.org/dev/peps/pep-0255/ +RedirectTemp /pep342 https://www.python.org/dev/peps/pep-0342/ +RedirectTemp /pep343 https://www.python.org/dev/peps/pep-0343/ +RedirectTemp /pep357 https://www.python.org/dev/peps/pep-0357/ +RedirectTemp /pep362 https://www.python.org/dev/peps/pep-0362/ +RedirectTemp /pep371 https://www.python.org/dev/peps/pep-0371/ +RedirectTemp /pep380 https://www.python.org/dev/peps/pep-0380/ +RedirectTemp /pep393 https://www.python.org/dev/peps/pep-0393/ +RedirectTemp /pep412 https://www.python.org/dev/peps/pep-0412/ +RedirectTemp /pep442 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /pep443 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /pep448 https://www.python.org/dev/peps/pep-0448/ +RedirectTemp /pep455 https://www.python.org/dev/peps/pep-0455/ +RedirectTemp /pep456 https://www.python.org/dev/peps/pep-0456/ +RedirectTemp /pep461 https://www.python.org/dev/peps/pep-0461/ +RedirectTemp /pep465 https://www.python.org/dev/peps/pep-0465/ +RedirectTemp /pep467 https://www.python.org/dev/peps/pep-0467/ +RedirectTemp /pep482 https://www.python.org/dev/peps/pep-0482/ +RedirectTemp /pep483 https://www.python.org/dev/peps/pep-0483/ +RedirectTemp /pep484 https://www.python.org/dev/peps/pep-0484/ +RedirectTemp /pep487 https://www.python.org/dev/peps/pep-0487/ +RedirectTemp /pep492 https://www.python.org/dev/peps/pep-0492/ +RedirectTemp /pep519 https://www.python.org/dev/peps/pep-0519/ +RedirectTemp /pep525 https://www.python.org/dev/peps/pep-0525/ +RedirectTemp /pep526 https://www.python.org/dev/peps/pep-0526/ +RedirectTemp /pep528 https://www.python.org/dev/peps/pep-0528/ +RedirectTemp /pep529 https://www.python.org/dev/peps/pep-0529/ +RedirectTemp /pep530 https://www.python.org/dev/peps/pep-0530/ +RedirectTemp /pep544 https://www.python.org/dev/peps/pep-0544/ +RedirectTemp /pep554 https://www.python.org/dev/peps/pep-0554/ +RedirectTemp /pep557 https://www.python.org/dev/peps/pep-0557/ +RedirectTemp /pep560 https://www.python.org/dev/peps/pep-0560/ +RedirectTemp /pep561 https://www.python.org/dev/peps/pep-0561/ +RedirectTemp /pep563 https://www.python.org/dev/peps/pep-0563/ +RedirectTemp /pep570 https://www.python.org/dev/peps/pep-0570/ +RedirectTemp /pep572 https://www.python.org/dev/peps/pep-0572/ +RedirectTemp /pep584 https://www.python.org/dev/peps/pep-0584/ +RedirectTemp /pep585 https://www.python.org/dev/peps/pep-0585/ +RedirectTemp /pep586 https://www.python.org/dev/peps/pep-0586/ +RedirectTemp /pep589 https://www.python.org/dev/peps/pep-0589/ +RedirectTemp /pep591 https://www.python.org/dev/peps/pep-0591/ +RedirectTemp /pep593 https://www.python.org/dev/peps/pep-0593/ +RedirectTemp /pep604 https://www.python.org/dev/peps/pep-0604/ +RedirectTemp /pep612 https://www.python.org/dev/peps/pep-0612/ +RedirectTemp /pep613 https://www.python.org/dev/peps/pep-0613/ +RedirectTemp /pep616 https://www.python.org/dev/peps/pep-0616/ +RedirectTemp /pep617 https://www.python.org/dev/peps/pep-0617/ +RedirectTemp /pep618 https://www.python.org/dev/peps/pep-0618/ +RedirectTemp /pep634 https://www.python.org/dev/peps/pep-0634/ +RedirectTemp /pep635 https://www.python.org/dev/peps/pep-0635/ +RedirectTemp /pep636 https://www.python.org/dev/peps/pep-0636/ +RedirectTemp /pep638 https://www.python.org/dev/peps/pep-0638/ +RedirectTemp /pep645 https://www.python.org/dev/peps/pep-0645/ +RedirectTemp /pep646 https://www.python.org/dev/peps/pep-0646/ +RedirectTemp /pep647 https://www.python.org/dev/peps/pep-0647/ +RedirectTemp /pep649 https://www.python.org/dev/peps/pep-0649/ +RedirectTemp /pep654 https://www.python.org/dev/peps/pep-0654/ +RedirectTemp /pep655 https://www.python.org/dev/peps/pep-0655/ +RedirectTemp /pep661 https://www.python.org/dev/peps/pep-0661/ +RedirectTemp /pep3099 https://www.python.org/dev/peps/pep-3099/ +RedirectTemp /pep3102 https://www.python.org/dev/peps/pep-3102/ +RedirectTemp /pep3104 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /pep3106 https://www.python.org/dev/peps/pep-3106/ +RedirectTemp /pep3107 https://www.python.org/dev/peps/pep-3107/ +RedirectTemp /pep3115 https://www.python.org/dev/peps/pep-3115/ +RedirectTemp /pep3118 https://www.python.org/dev/peps/pep-3118/ +RedirectTemp /pep3119 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /pep3129 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /pep3132 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /pep3141 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /pep3148 https://www.python.org/dev/peps/pep-3148/ +RedirectTemp /pep3155 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /pep3333 https://www.python.org/dev/peps/pep-3333/ + +# Remaining URLs by chapter + +############################################################ p +RedirectTemp /p-1 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /p-2 https://docs.python.org/3.10/tutorial/ +RedirectTemp /p-3 https://docs.python.org/3/tutorial/ +RedirectTemp /p-4 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-5 https://www.oreilly.com/online-learning/try-now.html +RedirectTemp /p-6 https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/ +RedirectTemp /p-7 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /p-8 https://pythonpro.com.br +RedirectTemp /p-9 https://groups.google.com/g/python-brasil +RedirectTemp /p-10 https://www.coffeelab.com.br/ +RedirectTemp /p-11 https://garoa.net.br/wiki/P%C3%A1gina_principal +############################################################ a +RedirectTemp /a-1 https://groups.google.com/forum/#!topic/python-tulip/Y4bhLNbKs74 +RedirectTemp /a-2 https://docs.python.org/3/library/asyncio-eventloop.html#executor +RedirectTemp /a-3 https://www.youtube.com/watch?v=x-kB2o8sd5c +RedirectTemp /a-4 https://www.youtube.com/watch?v=OSGv2VnC0go +RedirectTemp /a-5 https://mail.python.org/pipermail/python-ideas/2015-March/032557.html +RedirectTemp /a-6 https://pypi.org/project/pep8/ +RedirectTemp /a-7 https://pypi.org/project/flake8/ +RedirectTemp /a-8 https://pypi.org/project/pyflakes/ +RedirectTemp /a-9 https://pypi.org/project/mccabe/ +RedirectTemp /a-10 https://google.github.io/styleguide/pyguide.html +RedirectTemp /a-11 https://flask.palletsprojects.com/en/1.1.x/styleguide/ +RedirectTemp /a-12 https://docs.python-guide.org/ +RedirectTemp /a-13 https://david.goodger.org/projects/pycon/2007/idiomatic/handout.html +RedirectTemp /a-14 https://docs.mongodb.com/manual/about/#about-the-documentation-process +RedirectTemp /a-15 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /a-16 https://mail.python.org/pipermail/tutor/2003-October/thread.html#25930 +RedirectTemp /a-17 https://mail.python.org/pipermail/python-list/2003-April/192027.html +RedirectTemp /a-18 https://www.python.org/doc/essays/ +############################################################ 01 +RedirectTemp /1-1 http://hugunin.net/story_of_jython.html +RedirectTemp /1-2 https://www.oreilly.com/library/view/jython-essentials/9781449397364/ +RedirectTemp /1-3 https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers +RedirectTemp /1-4 https://docs.python.org/3.10/library/string.html#format-string-syntax +RedirectTemp /1-5 https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr +RedirectTemp /1-6 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /1-7 https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists +RedirectTemp /1-8 https://www.python.org/doc/humor/#the-zen-of-python +RedirectTemp /1-9 https://stackoverflow.com/users/95810/alex-martelli +RedirectTemp /1-10 https://en.wikipedia.org/wiki/Object_model +RedirectTemp /1-11 https://www.dourish.com/goodies/jargon.html +RedirectTemp /1-12 https://zopeinterface.readthedocs.io/en/latest/ +RedirectTemp /1-13 https://plone.org/ +############################################################ 02 +RedirectTemp /2-1 https://github.com/fluentpython/example-code-2e/blob/master/02-array-seq/listcomp_speed.py +RedirectTemp /2-2 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-3 https://stackoverflow.com/questions/68630/are-tuples-more-efficient-than-lists-in-python/22140115#22140115 +RedirectTemp /2-4 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-5 https://docs.python.org/3/whatsnew/3.5.html#pep-448-additional-unpacking-generalizations +RedirectTemp /2-6 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-7 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-8 https://en.wikipedia.org/wiki/Switch_statement#Fallthrough +RedirectTemp /2-9 https://en.wikipedia.org/wiki/Dangling_else +RedirectTemp /2-10 https://github.com/gvanrossum/patma/blob/3ece6444ef70122876fd9f0099eb9490a2d630df/EXAMPLES.md#case-6-a-very-deep-iterable-and-type-match-with-extraction +RedirectTemp /2-11 https://github.com/fluentpython/lispy/blob/main/original/norvig/lis.py +RedirectTemp /2-12 https://norvig.com/lispy.html +RedirectTemp /2-13 https://numpy.org/doc/stable/user/quickstart.html#indexing-slicing-and-iterating +RedirectTemp /2-14 https://pythontutor.com/ +RedirectTemp /2-15 https://en.wikipedia.org/wiki/Fluent_interface +RedirectTemp /2-16 https://docs.python.org/3/library/bisect.html#bisect.insort +RedirectTemp /2-17 https://stackoverflow.com/questions/4845418/when-should-a-memoryview-be-used/ +RedirectTemp /2-18 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /2-19 http://www.netlib.org +RedirectTemp /2-20 https://pandas.pydata.org/ +RedirectTemp /2-21 https://scikit-learn.org/stable/ +RedirectTemp /2-22 https://docs.python.org/3/howto/sorting.html +RedirectTemp /2-23 https://www.python.org/dev/peps/pep-3132/ +RedirectTemp /2-24 https://bugs.python.org/issue2292 +RedirectTemp /2-25 https://docs.python.org/3.10/whatsnew/3.10.html#pep-634-structural-pattern-matching +RedirectTemp /2-26 https://docs.python.org/3.10/whatsnew/3.10.html +RedirectTemp /2-27 https://www.python.org/dev/peps/pep-0636/#appendix-a-quick-intro +RedirectTemp /2-28 https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews/ +RedirectTemp /2-29 https://jakevdp.github.io/PythonDataScienceHandbook/ +RedirectTemp /2-30 https://www.oreilly.com/library/view/python-for-data/9781491957653/ +RedirectTemp /2-31 https://www.labri.fr/perso/nrougier/from-python-to-numpy/ +RedirectTemp /2-32 https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html +RedirectTemp /2-33 http://www.fonts101.com/fonts/view/Uncategorized/34398/Dijkstra +RedirectTemp /2-34 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /2-35 https://en.wikipedia.org/wiki/Timsort +RedirectTemp /2-36 http://www.groklaw.net/pdf3/OraGoogle-1202.pdf +RedirectTemp /2-37 https://www.python.org/doc/humor/#id9 +############################################################ 03 +RedirectTemp /3-1 https://www.python.org/dev/peps/pep-0584/#motivation +RedirectTemp /3-2 https://docs.python.org/3.10/c-api/typeobj.html#Py_TPFLAGS_MAPPING +RedirectTemp /3-3 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-4 https://docs.python.org/3/glossary.html#term-hashable +RedirectTemp /3-5 http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf +RedirectTemp /3-6 https://github.com/pingo-io/pingo-py +RedirectTemp /3-7 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/missing.py +RedirectTemp /3-8 https://docs.python.org/3/library/collections.html#collections.ChainMap +RedirectTemp /3-9 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /3-10 https://docs.python.org/3/library/shelve.html +RedirectTemp /3-11 https://docs.python.org/3/library/dbm.html +RedirectTemp /3-12 https://docs.python.org/3/library/pickle.html +RedirectTemp /3-13 https://nedbatchelder.com/blog/202006/pickles_nine_flaws.html +RedirectTemp /3-14 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py#L813 +RedirectTemp /3-15 https://mail.python.org/pipermail/python-dev/2015-May/140003.html +RedirectTemp /3-16 https://bugs.python.org/issue18986 +RedirectTemp /3-17 https://github.com/fluentpython/example-code-2e/blob/master/03-dict-set/transformdict.py +RedirectTemp /3-18 http://gandenberger.org/2018/03/10/ordered-dicts-vs-ordereddict/ +RedirectTemp /3-19 https://www.pypy.org/ +RedirectTemp /3-20 https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html +RedirectTemp /3-21 https://www.npopov.com/2014/12/22/PHPs-new-hashtable-implementation.html +RedirectTemp /3-22 https://www.youtube.com/watch?v=66P5FMkWoVU +RedirectTemp /3-23 https://pyvideo.org/video/276/the-mighty-dictionary-55/ +RedirectTemp /3-24 https://www.youtube.com/watch?v=p33CVV29OG8 +RedirectTemp /3-25 https://docs.python.org/3/whatsnew/3.6.html#new-dict-implementation +RedirectTemp /3-26 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictobject.c +RedirectTemp /3-27 https://github.com/python/cpython/blob/cf7eaa4617295747ee5646c4e2b7e7a16d7c64ab/Objects/dictnotes.txt +RedirectTemp /3-28 https://www.youtube.com/watch?v=tGAngdU_8D8 +RedirectTemp /3-29 https://speakerdeck.com/ramalho/python-set-practice-at-pycon +RedirectTemp /3-30 https://github.com/standupdev/uintset +RedirectTemp /3-31 https://spectrum.ieee.org/hans-peter-luhn-and-the-birth-of-the-hashing-algorithm +RedirectTemp /3-32 http://www.json.org/fatfree.html +RedirectTemp /3-33 https://twitter.com/mitsuhiko/status/1229385843585974272 +############################################################ 04 +RedirectTemp /4-1 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-2 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-3 https://www.fluentpython.com/extra/parsing-binary-struct/ +RedirectTemp /4-4 https://www.fluentpython.com/extra/multi-character-emojis/ +RedirectTemp /4-5 https://w3techs.com/technologies/overview/character_encoding +RedirectTemp /4-6 https://docs.python.org/3/library/codecs.html#codecs.register_error +RedirectTemp /4-7 https://docs.python.org/3/library/stdtypes.html#str.isascii +RedirectTemp /4-8 https://pypi.org/project/chardet/ +RedirectTemp /4-9 https://docs.python.org/3/library/codecs.html#encodings-and-unicode +RedirectTemp /4-10 https://nedbatchelder.com/text/unipain/unipain.html +RedirectTemp /4-11 https://devblogs.microsoft.com/commandline/windows-command-line-unicode-and-utf-8-output-text-buffer/ +RedirectTemp /4-12 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING +RedirectTemp /4-13 https://docs.python.org/3/using/cmdline.html#envvar-PYTHONLEGACYWINDOWSSTDIO +RedirectTemp /4-14 https://docs.python.org/3/library/locale.html#locale.getpreferredencoding +RedirectTemp /4-15 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-16 https://docs.python.org/3/library/locale.html?highlight=strxfrm#locale.strxfrm +RedirectTemp /4-17 https://pypi.org/project/pyuca/ +RedirectTemp /4-18 https://github.com/jtauber/pyuca +RedirectTemp /4-19 http://www.unicode.org/Public/UCA/6.3.0/allkeys.txt +RedirectTemp /4-20 https://pypi.org/project/PyICU/ +RedirectTemp /4-21 https://docs.python.org/3.10/library/stdtypes.html#str.isalpha +RedirectTemp /4-22 https://en.wikipedia.org/wiki/Unicode_character_property#General_Category +RedirectTemp /4-23 https://en.wikipedia.org/wiki/Unicode_character_property +RedirectTemp /4-24 https://github.com/microsoft/terminal +RedirectTemp /4-25 https://docs.python.org/3/library/unicodedata.html +RedirectTemp /4-26 https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation +RedirectTemp /4-27 https://docs.python.org/3/library/re.html +RedirectTemp /4-28 https://nedbatchelder.com/text/unipain.html +RedirectTemp /4-29 https://www.slideshare.net/fischertrav/character-encoding-unicode-how-to-with-dignity-33352863 +RedirectTemp /4-30 https://pyvideo.org/video/2625/character-encoding-and-unicode-in-python/ +RedirectTemp /4-31 https://regebro.wordpress.com/2011/03/23/unconfusing-unicode-what-is-unicode/ +RedirectTemp /4-32 https://docs.python.org/3/howto/unicode.html +RedirectTemp /4-33 https://diveintopython3.net/strings.html +RedirectTemp /4-34 https://diveintopython3.net/ +RedirectTemp /4-35 https://finderiko.com/python-book +RedirectTemp /4-36 https://docs.python.org/3.0/whatsnew/3.0.html#text-vs-data-instead-of-unicode-vs-8-bit +RedirectTemp /4-37 https://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ +RedirectTemp /4-38 http://python-notes.curiousefficiency.org/en/latest/python3/binary_protocols.html +RedirectTemp /4-39 http://python-notes.curiousefficiency.org/en/latest/python3/text_file_processing.html +RedirectTemp /4-40 https://docs.python.org/3/library/codecs.html#standard-encodings +RedirectTemp /4-41 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Tools/unicode/listcodecs.py +RedirectTemp /4-42 https://www.oreilly.com/library/view/unicode-explained/059610121X/ +RedirectTemp /4-43 https://www.informit.com/store/unicode-demystified-a-practical-programmers-guide-to-9780201700527 +RedirectTemp /4-44 https://unicodebook.readthedocs.io/index.html +RedirectTemp /4-45 https://www.w3.org/International/wiki/Case_folding +RedirectTemp /4-46 http://www.w3.org/TR/charmod-norm/ +RedirectTemp /4-47 http://unicode.org/reports/tr15/ +RedirectTemp /4-48 http://www.unicode.org/faq/normalization.html +RedirectTemp /4-49 http://www.unicode.org/ +RedirectTemp /4-50 http://www.macchiato.com/unicode/nfc-faq +RedirectTemp /4-51 https://stories.moma.org/the-original-emoji-set-has-been-added-to-the-museum-of-modern-arts-collection-c6060e141f61?gi=c403f5a840a5 +RedirectTemp /4-52 https://emojipedia.org/ +RedirectTemp /4-53 https://blog.emojipedia.org/correcting-the-record-on-the-first-emoji-set/ +RedirectTemp /4-54 http://emojitracker.com/ +RedirectTemp /4-55 http://www.unicode.org/glossary/#plain_text +RedirectTemp /4-56 http://www.methods.co.nz/asciidoc/ +RedirectTemp /4-57 https://atlas.oreilly.com/ +############################################################ 05 +RedirectTemp /5-1 https://docs.python.org/3/library/typing.html#typing.TypedDict +RedirectTemp /5-2 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /5-3 https://docs.python.org/3/library/typing.html#typing.get_type_hints +RedirectTemp /5-4 https://docs.python.org/3.8/library/collections.html#collections.somenamedtuple._asdict +RedirectTemp /5-5 https://www.jetbrains.com/pycharm/ +RedirectTemp /5-6 https://www.python.org/dev/peps/pep-0484/#acceptable-type-hints +RedirectTemp /5-7 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-8 https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass +RedirectTemp /5-9 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-10 https://docs.python.org/3/library/dataclasses.html#inheritance +RedirectTemp /5-11 https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations +RedirectTemp /5-12 https://dublincore.org/specifications/dublin-core/ +RedirectTemp /5-13 https://en.wikipedia.org/wiki/Dublin_Core +RedirectTemp /5-14 https://martinfowler.com/bliki/CodeSmell.html +RedirectTemp /5-15 https://martinfowler.com/books/refactoring.html +RedirectTemp /5-16 https://www.python.org/dev/peps/pep-0634/#class-patterns +RedirectTemp /5-17 https://docs.python.org/3/library/dataclasses.html +RedirectTemp /5-18 https://www.python.org/dev/peps/pep-0557/#id47 +RedirectTemp /5-19 https://www.python.org/dev/peps/pep-0557/#id48 +RedirectTemp /5-20 https://www.python.org/dev/peps/pep-0557/#id33 +RedirectTemp /5-21 https://realpython.com +RedirectTemp /5-22 https://realpython.com/python-data-classes/ +RedirectTemp /5-23 https://www.youtube.com/watch?v=T-TwcmT6Rcw +RedirectTemp /5-24 https://www.attrs.org/en/stable/ +RedirectTemp /5-25 https://glyph.twistedmatrix.com/2016/08/attrs.html +RedirectTemp /5-26 https://www.attrs.org/en/stable/why.html +RedirectTemp /5-27 https://github.com/dabeaz/cluegen +RedirectTemp /5-28 https://refactoring.guru/ +RedirectTemp /5-29 https://refactoring.guru/smells/data-class +RedirectTemp /5-30 https://web.archive.org/web/20190204130328/http://catb.org/esr/jargon/html/G/Guido.html +RedirectTemp /5-31 https://web.archive.org/web/20190211161610/http://catb.org/esr/jargon/html/index.html +RedirectTemp /5-32 https://www.attrs.org/en/stable/ +############################################################ 06 +RedirectTemp /6-1 https://www.olin.edu/faculty/profile/lynn-andrea-stein/ +RedirectTemp /6-2 https://docs.python.org/3/reference/datamodel.html#objects-values-and-types +RedirectTemp /6-3 https://pythontutor.com/ +RedirectTemp /6-4 https://docs.python.org/3/library/copy.html +RedirectTemp /6-5 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /6-6 https://docs.python.org/3/reference/datamodel.html#object.%5C_%5C_del__ +RedirectTemp /6-7 https://emptysqua.re/blog/pypy-garbage-collection-and-a-deadlock/ +RedirectTemp /6-8 https://www.youtube.com/watch?v=HHFCFJSPWrI&feature=youtu.be +RedirectTemp /6-9 http://pymotw.com/3/copy/ +RedirectTemp /6-10 http://pymotw.com/3/weakref/ +RedirectTemp /6-11 https://docs.python.org/3/library/gc.html +RedirectTemp /6-12 https://devguide.python.org/garbage_collector/ +RedirectTemp /6-13 https://devguide.python.org/ +RedirectTemp /6-14 https://www.python.org/dev/peps/pep-0442/ +RedirectTemp /6-15 https://en.wikipedia.org/wiki/String_interning +RedirectTemp /6-16 https://en.wikipedia.org/wiki/Haddocks%27_Eyes +RedirectTemp /6-17 https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf +############################################################ 07 +RedirectTemp /7-1 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-2 https://www.fluentpython.com/extra/function-introspection/ +RedirectTemp /7-3 https://docs.python.org/3/library/functions.html#map +RedirectTemp /7-4 https://en.wikipedia.org/wiki/Functional_programming +RedirectTemp /7-5 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-6 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-7 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-8 https://docs.python.org/3/whatsnew/3.8.html#positional-only-parameters +RedirectTemp /7-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/functools.py#L341 +RedirectTemp /7-10 https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy +RedirectTemp /7-11 https://docs.python.org/3/howto/functional.html +RedirectTemp /7-12 https://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessary +RedirectTemp /7-13 https://speakerdeck.com/ramalho/beyond-paradigms-berlin-edition +RedirectTemp /7-14 https://www.youtube.com/watch?v=bF3a2VYXxa0 +RedirectTemp /7-15 http://cs.brown.edu/~sk/Publications/Papers/Published/sk-teach-pl-post-linnaean/ +RedirectTemp /7-16 http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html +RedirectTemp /7-17 https://raw.githubusercontent.com/python/cpython/main/Misc/HISTORY +RedirectTemp /7-18 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +############################################################ 08 +RedirectTemp /8-1 https://www.python.org/dev/peps/pep-0484/#non-goals +RedirectTemp /8-2 https://github.com/python/typing/issues/182 +RedirectTemp /8-3 https://github.com/python/mypy/issues/731 +RedirectTemp /8-4 https://github.com/google/pytype +RedirectTemp /8-5 https://github.com/Microsoft/pyright +RedirectTemp /8-6 https://pyre-check.org/ +RedirectTemp /8-7 https://mypy.readthedocs.io/en/stable/introduction.html +RedirectTemp /8-8 https://mypy.readthedocs.io/en/stable/config_file.html +RedirectTemp /8-9 https://pypi.org/project/flake8/ +RedirectTemp /8-10 https://pypi.org/project/blue/ +RedirectTemp /8-11 https://pypi.org/project/black/ +RedirectTemp /8-12 https://wefearchange.org/2020/11/steeringcouncil.rst.html +RedirectTemp /8-13 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /8-14 https://en.wikipedia.org/wiki/Barbara_Liskov +RedirectTemp /8-15 https://en.wikipedia.org/wiki/Behavioral_subtyping +RedirectTemp /8-16 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-17 https://docs.python.org/3/library/typing.html#module-contents +RedirectTemp /8-18 https://en.wikipedia.org/wiki/Geohash +RedirectTemp /8-19 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /8-20 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-21 https://docs.python.org/3/library/typing.html#typing.Dict +RedirectTemp /8-22 https://docs.python.org/3/library/typing.html#typing.Set +RedirectTemp /8-23 https://www.python.org/dev/peps/pep-0585/#implementation +RedirectTemp /8-24 https://docs.python.org/3/library/numbers.html +RedirectTemp /8-25 https://docs.python.org/3/library/typing.html#typing.List +RedirectTemp /8-26 https://github.com/python/typeshed +RedirectTemp /8-27 https://github.com/python/typeshed/blob/66cd36268a6a667714efaa27198a41d0d7f89477/stdlib/2and3/math.pyi#L45 +RedirectTemp /8-28 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-29 https://github.com/python/cpython/blob/822efa5695b5ba6c2316c1400e4e9ec2546f7ea5/Lib/statistics.py#L534 +RedirectTemp /8-30 https://github.com/python/typeshed/blob/e1e99245bb46223928eba68d4fc74962240ba5b4/stdlib/3/statistics.pyi +RedirectTemp /8-31 https://docs.python.org/3/library/statistics.html#statistics.mode +RedirectTemp /8-32 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/3/statistics.pyi#L32 +RedirectTemp /8-33 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /8-34 https://docs.python.org/3/library/typing.html#typing.Callable +RedirectTemp /8-35 https://pypi.org/project/blue/ +RedirectTemp /8-36 https://www.python.org/dev/peps/pep-0484/#id38 +RedirectTemp /8-37 https://docs.google.com/document/d/1aXs1tpwzPjW9MdsG5dI7clNFyYayFBkcXwRDo-qvbIk/preview +RedirectTemp /8-38 https://www.oreilly.com/library/view/the-best-software/9781590595008/ +RedirectTemp /8-39 https://www.youtube.com/watch?v=YFexUDjHO6w +RedirectTemp /8-40 https://www.youtube.com/watch?v=YFexUDjHO6w&t=13m40s +RedirectTemp /8-41 https://bernat.tech/posts/the-state-of-type-hints-in-python/ +RedirectTemp /8-42 https://realpython.com/python-type-checking/ +RedirectTemp /8-43 https://cjolowicz.github.io/posts/hypermodern-python-04-typing/ +RedirectTemp /8-44 https://mypy.readthedocs.io/en/stable/index.html +RedirectTemp /8-45 https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html +RedirectTemp /8-46 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /8-47 https://github.com/typeddjango/awesome-python-typing +RedirectTemp /8-48 https://docs.python.org/3/library/functions.html#max +RedirectTemp /8-49 https://en.wikipedia.org/wiki/Linguistic_relativity +RedirectTemp /8-50 https://pypistats.org/top +RedirectTemp /8-51 https://github.com/psf/requests/issues/3855 +RedirectTemp /8-52 https://lwn.net/Articles/643399/ +RedirectTemp /8-53 https://docs.python-requests.org/en/master/api/#requests.request +RedirectTemp /8-54 https://queue.acm.org/detail.cfm?id=1039523 +############################################################ 09 +RedirectTemp /9-1 https://docs.python.org/3/library/dis.html +RedirectTemp /9-2 https://en.wikipedia.org/wiki/Memoization +RedirectTemp /9-3 https://numpy.org/doc/stable/user/basics.types.html +RedirectTemp /9-4 https://docs.python.org/3/library/functools.html#functools.singledispatch +RedirectTemp /9-5 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/README.md +RedirectTemp /9-6 https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/01-how-you-implemented-your-python-decorator-is-wrong.md +RedirectTemp /9-7 https://wrapt.readthedocs.io/en/latest/ +RedirectTemp /9-8 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch09.html +RedirectTemp /9-9 https://pypi.org/project/decorator/ +RedirectTemp /9-10 https://wiki.python.org/moin/PythonDecoratorLibrary +RedirectTemp /9-11 http://web.archive.org/web/20201109032203/http://effbot.org/zone/closure.htm +RedirectTemp /9-12 https://www.python.org/dev/peps/pep-3104/ +RedirectTemp /9-13 https://www.python.org/dev/peps/pep-0227/ +RedirectTemp /9-14 https://www.python.org/dev/peps/pep-0443/ +RedirectTemp /9-15 https://www.artima.com/weblogs/viewpost.jsp?thread=101605 +RedirectTemp /9-16 https://reg.readthedocs.io/en/latest/ +RedirectTemp /9-17 https://morepath.readthedocs.io/en/latest/ +RedirectTemp /9-18 https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Binding.html +RedirectTemp /9-19 http://www.paulgraham.com/rootsoflisp.html +RedirectTemp /9-20 http://www-formal.stanford.edu/jmc/recursive/recursive.html +RedirectTemp /9-21 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +############################################################ 10 +RedirectTemp /10-1 https://en.wikipedia.org/wiki/Software_design_pattern +RedirectTemp /10-2 https://en.wikipedia.org/wiki/Iterator_pattern +RedirectTemp /10-3 https://github.com/python/mypy/issues/9397 +RedirectTemp /10-4 http://www.norvig.com/design-patterns/index.htm +RedirectTemp /10-5 https://pyvideo.org/video/1110/python-design-patterns/ +RedirectTemp /10-6 http://www.aleax.it/gdd_pydp.pdf +RedirectTemp /10-7 https://perl.plover.com/yak/design/ +RedirectTemp /10-8 https://en.wikipedia.org/wiki/Turtles_all_the_way_down +############################################################ 11 +RedirectTemp /11-1 https://blog.startifact.com/posts/older/what-is-pythonic.html +RedirectTemp /11-2 https://julien.danjou.info/guide-python-static-class-abstract-methods/ +RedirectTemp /11-3 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-4 https://docs.python.org/3/reference/lexical_analysis.html#f-strings +RedirectTemp /11-5 https://docs.python.org/3/library/string.html#format-string-syntax +RedirectTemp /11-6 https://docs.python.org/3/library/string.html#formatspec +RedirectTemp /11-7 https://docs.python.org/3/reference/datamodel.html#object.__hash__ +RedirectTemp /11-8 https://web.archive.org/web/20161025185040/http://pythonpaste.org/StyleGuide.html +RedirectTemp /11-9 https://docs.python.org/3/tutorial/modules.html#more-on-modules +RedirectTemp /11-10 https://docs.python.org/3/library/gettext.html#gettext.NullTranslations +RedirectTemp /11-11 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/mem_test.py +RedirectTemp /11-12 https://docs.python.org/3/reference/datamodel.html#basic-customization +RedirectTemp /11-13 http://esug.org/data/HistoricalDocuments/TheSmalltalkReport/ST07/04wo.pdf +RedirectTemp /11-14 https://www.artima.com/articles/the-simplest-thing-that-could-possibly-work#part3 +RedirectTemp /11-15 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +RedirectTemp /11-16 https://github.com/fluentpython/example-code-2e/blob/master/11-pythonic-obj/private/Expose.java +RedirectTemp /11-17 https://docs.oracle.com/javase/tutorial/essential/environment/security.html +############################################################ 12 +RedirectTemp /12-1 https://en.wikipedia.org/wiki/Vector_space_model +RedirectTemp /12-2 https://pypi.org/project/gensim/ +RedirectTemp /12-3 https://docs.python.org/3/library/functions.html#enumerate +RedirectTemp /12-4 https://mathworld.wolfram.com/Hypersphere.html +RedirectTemp /12-5 https://en.wikipedia.org/wiki/Fold_(higher-order_function) +RedirectTemp /12-6 https://docs.python.org/2.5/whatsnew/pep-357.html +RedirectTemp /12-7 https://docs.python.org/3/reference/datamodel.html#special-method-names +RedirectTemp /12-8 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /12-9 https://mail.python.org/pipermail/python-list/2000-July/046184.html +RedirectTemp /12-10 https://en.wikipedia.org/wiki/Duck_typing +RedirectTemp /12-11 https://mail.python.org/mailman/listinfo/python-list +RedirectTemp /12-12 https://mail.python.org/pipermail/python-list/2003-April/218568.html +############################################################ 13 +RedirectTemp /13-1 https://docs.python.org/3/c-api/index.html +RedirectTemp /13-2 https://docs.python.org/3/c-api/sequence.html +RedirectTemp /13-3 https://github.com/python/cpython/blob/31ceccb2c77854893f3a754aca04bedd74bedb10/Lib/_collections_abc.py#L870 +RedirectTemp /13-4 https://en.wikipedia.org/wiki/Monkey_patch +RedirectTemp /13-5 https://www.gevent.org/api/gevent.monkey.html +RedirectTemp /13-6 https://docs.python.org/3/library/random.html#random.shuffle +RedirectTemp /13-7 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /13-8 https://docs.python.org/3/library/collections.html#collections.namedtuple +RedirectTemp /13-9 https://github.com/python/typeshed/blob/24afb531ffd07083d6a74be917342195062f7277/stdlib/collections/__init__.pyi +RedirectTemp /13-10 https://docs.python.org/3/glossary.html#term-abstract-base-class +RedirectTemp /13-11 https://en.wikipedia.org/wiki/Duck_typing#History +RedirectTemp /13-12 http://ptgmedia.pearsoncmg.com/images/020163371x/items/item33.html +RedirectTemp /13-13 https://docs.python.org/3/library/bisect.html#bisect.bisect +RedirectTemp /13-14 https://github.com/python/cpython/blob/main/Lib/_collections_abc.py +RedirectTemp /13-15 https://github.com/python/cpython/blob/main/Lib/abc.py +RedirectTemp /13-16 https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes +RedirectTemp /13-17 https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable +RedirectTemp /13-18 https://docs.python.org/3/library/abc.html +RedirectTemp /13-19 https://docs.python.org/dev/library/abc.html#abc.abstractmethod +RedirectTemp /13-20 https://docs.python.org/dev/library/abc.html +RedirectTemp /13-21 https://docs.python.org/3/library/os.html#os.urandom +RedirectTemp /13-22 https://github.com/python/mypy/issues/2922 +RedirectTemp /13-23 https://docs.python.org/3/library/stdtypes.html#truth +RedirectTemp /13-24 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Lib/_collections_abc.py +RedirectTemp /13-25 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L369 +RedirectTemp /13-26 https://github.com/python/cpython/blob/c0a9afe2ac1820409e6173bd1893ebee2cf50270/Lib/abc.py#L196 +RedirectTemp /13-27 https://bugs.python.org/issue31333 +RedirectTemp /13-28 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Modules/_abc.c#L605 +RedirectTemp /13-29 https://github.com/python/cpython/blob/0fbddb14dc03f61738af01af88e7d8aa8df07336/Lib/_collections_abc.py#L881 +RedirectTemp /13-30 https://docs.python.org/3/library/typing.html#protocols +RedirectTemp /13-31 https://github.com/python/cpython/blob/3635388f52b42e5280229104747962117104c453/Lib/typing.py#L1751 +RedirectTemp /13-32 https://mail.python.org/archives/list/python-dev@python.org/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ +RedirectTemp /13-33 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-34 https://en.wikipedia.org/wiki/Interface_segregation_principle +RedirectTemp /13-35 https://github.com/python/typeshed/blob/master/CONTRIBUTING.md +RedirectTemp /13-36 https://gist.github.com/asukakenji/ac8a05644a2e98f1d5ea8c299541fce9 +RedirectTemp /13-37 https://www.python.org/dev/peps/pep-0544/#runtime-checkable-decorator-and-narrowing-types-by-isinstance +RedirectTemp /13-38 https://www.python.org/dev/peps/pep-0544/#merging-and-extending-protocols +RedirectTemp /13-39 https://numpy.org/devdocs/user/basics.types.html +RedirectTemp /13-40 https://github.com/python/typeshed/blob/master/stdlib/statistics.pyi +RedirectTemp /13-41 https://bugs.python.org/issue41974 +RedirectTemp /13-42 https://glyph.twistedmatrix.com/2020/07/new-duck.html +RedirectTemp /13-43 https://glyph.twistedmatrix.com/2021/03/interfaces-and-protocols.html +RedirectTemp /13-44 https://plone.org/ +RedirectTemp /13-45 https://trypyramid.com/ +RedirectTemp /13-46 https://twistedmatrix.com/trac/ +RedirectTemp /13-47 https://www.artima.com/articles/contracts-in-python +RedirectTemp /13-48 https://martinfowler.com/bliki/DynamicTyping.html +RedirectTemp /13-49 https://martinfowler.com/bliki/RoleInterface.html +RedirectTemp /13-50 https://mypy.readthedocs.io/en/stable/protocols.html +RedirectTemp /13-51 https://pymotw.com/3/abc/index.html +RedirectTemp /13-52 https://www.python.org/dev/peps/pep-3119/ +RedirectTemp /13-53 https://www.python.org/dev/peps/pep-3141/ +RedirectTemp /13-54 https://docs.python.org/3/library/numbers.html +RedirectTemp /13-55 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-56 https://stackoverflow.com/questions/69334475/how-to-hint-at-number-types-i-e-subclasses-of-number-not-numbers-themselv/69383462#69383462 +RedirectTemp /13-57 https://github.com/python/mypy/issues/3186 +RedirectTemp /13-58 https://martinfowler.com/articles/lean-inception/ +RedirectTemp /13-59 https://martinfowler.com +RedirectTemp /13-60 https://www.jetbrains.com/pycharm/ +RedirectTemp /13-61 https://wingware.com/ +RedirectTemp /13-62 https://code.visualstudio.com/ +############################################################ 14 +RedirectTemp /14-1 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-2 https://docs.python.org/3/tutorial/classes.html +RedirectTemp /14-3 https://docs.python.org/3/library/collections.html#ordereddict-examples-and-recipes +RedirectTemp /14-4 https://discuss.python.org/t/is-it-time-to-deprecate-unbound-super-methods/1833 +RedirectTemp /14-5 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-6 https://docs.python.org/3/library/collections.html +RedirectTemp /14-7 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/strkeydict_dictsub.py +RedirectTemp /14-8 https://doc.pypy.org/en/latest/cpython_differences.html#subclasses-of-built-in-types +RedirectTemp /14-9 https://en.wikipedia.org/wiki/Breadth-first_search +RedirectTemp /14-10 https://www.python.org/download/releases/2.3/mro/ +RedirectTemp /14-11 https://github.com/fluentpython/example-code-2e/blob/master/14-inheritance/uppermixin.py +RedirectTemp /14-12 https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html +RedirectTemp /14-13 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-14 https://github.com/python/cpython/blob/8ece98a7e418c3c68a4c61bc47a2d0931b59a889/Lib/collections/__init__.py#L1084 +RedirectTemp /14-15 https://docs.python.org/3/library/http.server.html +RedirectTemp /14-16 https://github.com/python/cpython/blob/17c23167942498296f0bdfffe52e72d53d66d693/Lib/http/server.py#L144 +RedirectTemp /14-17 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L664 +RedirectTemp /14-18 https://docs.python.org/3/library/socketserver.html#socketserver.ForkingMixIn +RedirectTemp /14-19 https://docs.python.org/3/library/os.html#os.fork +RedirectTemp /14-20 https://en.wikipedia.org/wiki/POSIX +RedirectTemp /14-21 http://ccbv.co.uk/ +RedirectTemp /14-22 https://github.com/django/django/tree/main/django/views/generic +RedirectTemp /14-23 https://en.wikipedia.org/wiki/Template_method_pattern +RedirectTemp /14-24 https://docs.python.org/3/library/tkinter.html +RedirectTemp /14-25 https://docs.python.org/3/library/tkinter.ttk.html +RedirectTemp /14-26 https://docs.oracle.com/javase/10/docs/api/java/awt/package-tree.html +RedirectTemp /14-27 https://docs.oracle.com/javase/10/docs/api/javax/swing/package-tree.html +RedirectTemp /14-28 https://squeak.org/ +RedirectTemp /14-29 https://github.com/django/django/blob/b64db05b9cedd96905d637a2d824cbbf428e40e7/django/views/generic/list.py#L194 +RedirectTemp /14-30 https://github.com/python/cpython/blob/8ed183391241f0c73e7ba7f42b1d49fc02985f7b/Lib/tkinter/__init__.py#L2618 +RedirectTemp /14-31 https://docs.python.org/3/library/socketserver.html +RedirectTemp /14-32 https://docs.python.org/3/library/socketserver.html#socketserver.BaseServer +RedirectTemp /14-33 https://github.com/python/cpython/blob/699ee016af5736ffc80f68359617611a22b72943/Lib/socketserver.py#L153 +RedirectTemp /14-34 https://docs.python.org/3/library/typing.html#typing.final +RedirectTemp /14-35 https://docs.python.org/3/library/typing.html#typing.Final +RedirectTemp /14-36 https://docs.python.org/3/library/collections.abc.html +RedirectTemp /14-37 https://hynek.me/articles/python-subclassing-redux/ +RedirectTemp /14-38 https://www.oreilly.com/library/view/python-cookbook-3rd/9781449357337/ch08.html#super +RedirectTemp /14-39 https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ +RedirectTemp /14-40 https://fuhm.net/super-harmful/ +RedirectTemp /14-41 https://stackoverflow.com/questions/30190185/how-to-use-super-with-one-argument/30190341#30190341 +RedirectTemp /14-42 https://www.artima.com/weblogs/viewpost.jsp?thread=246488 +RedirectTemp /14-43 https://www.artima.com/weblogs/viewpost.jsp?thread=281127 +RedirectTemp /14-44 https://www.artima.com/weblogs/viewpost.jsp?thread=246341 +RedirectTemp /14-45 https://www.artima.com/weblogs/viewpost.jsp?thread=246483 +RedirectTemp /14-46 https://www.artima.com/weblogs/viewpost.jsp?thread=236275 +RedirectTemp /14-47 https://www.artima.com/weblogs/viewpost.jsp?thread=236278 +RedirectTemp /14-48 https://www.artima.com/weblogs/viewpost.jsp?thread=237121 +RedirectTemp /14-49 https://python-patterns.guide/gang-of-four/composition-over-inheritance/ +RedirectTemp /14-50 https://python-patterns.guide/ +RedirectTemp /14-51 https://www.youtube.com/watch?v=3MNVP9-hglc +RedirectTemp /14-52 http://worrydream.com/EarlyHistoryOfSmalltalk/ +RedirectTemp /14-53 https://en.wikipedia.org/wiki/Polymorphism_(computer_science) +############################################################ 15 +RedirectTemp /15-1 https://www.youtube.com/watch?v=csL8DLXGNlU&t=92m5s +RedirectTemp /15-2 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi#L1434 +RedirectTemp /15-3 https://github.com/python/typeshed/blob/a8834fcd46339e17fc8add82b5803a1ce53d3d60/stdlib/2and3/builtins.pyi +RedirectTemp /15-4 https://twitter.com/gwidion/status/1265384692464967680 +RedirectTemp /15-5 https://pypi.org/project/pydantic/ +RedirectTemp /15-6 https://google.github.io/pytype/faq.html +RedirectTemp /15-7 https://google.github.io/pytype/faq.html +RedirectTemp /15-8 https://lxml.de/ +RedirectTemp /15-9 https://docs.python.org/3/library/xml.etree.elementtree.html +RedirectTemp /15-10 https://mypy.readthedocs.io/en/stable/common_issues.html +RedirectTemp /15-11 https://mypy.readthedocs.io/en/stable/common_issues.html#types-of-empty-collections +RedirectTemp /15-12 https://github.com/python/typing/issues/182 +RedirectTemp /15-13 https://pypi.org/project/pydantic/ +RedirectTemp /15-14 https://mypy.readthedocs.io/en/stable/type_narrowing.html#casts +RedirectTemp /15-15 https://github.com/python/cpython/blob/bee66d3cb98e740f9d8057eb7f503122052ca5d8/Lib/typing.py#L1340 +RedirectTemp /15-16 https://www.python.org/dev/peps/pep-0484/#casts +RedirectTemp /15-17 https://github.com/python/typeshed/issues/5535 +RedirectTemp /15-18 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /15-19 https://github.com/python/cpython/blob/b798ab06937f8bb24b444a49dd42e11fff15e654/Lib/test/test_asyncio/test_server.py#L55 +RedirectTemp /15-20 https://en.wikipedia.org/wiki/Code_smell +RedirectTemp /15-21 https://mail.python.org/archives/list/typing-sig@python.org/message/5LCWMN2UY2UQNLC5Z47GHBZKSPZW4I63/ +RedirectTemp /15-22 https://mypy.readthedocs.io/en/stable/error_codes.html#error-codes +RedirectTemp /15-23 https://github.com/fluentpython/example-code-2e/blob/master/15-more-types/clip_annot.py +RedirectTemp /15-24 https://docs.python.org/3/library/typing.html#introspection-helpers +RedirectTemp /15-25 https://docs.python.org/3.10/library/inspect.html#inspect.get_annotations +RedirectTemp /15-26 https://www.python.org/dev/peps/pep-0563/#abstract +RedirectTemp /15-27 https://mail.python.org/archives/list/python-dev@python.org/message/ZBJ7MD6CSGM6LZAOTET7GXAVBZB7O77O/ +RedirectTemp /15-28 https://docs.python.org/3.10/howto/annotations.html +RedirectTemp /15-29 https://docs.python.org/3/library/typing.html#user-defined-generic-types +RedirectTemp /15-30 https://github.com/python/typeshed/blob/bfc83c365a0b26ab16586beac77ff16729d0e473/stdlib/builtins.pyi#L743 +RedirectTemp /15-31 https://docs.python.org/3.10/library/typing.html#typing.FrozenSet +RedirectTemp /15-32 https://docs.python.org/3.10/library/typing.html#typing.Generator +RedirectTemp /15-33 https://docs.python.org/3.10/library/typing.html#typing.AsyncGenerator +RedirectTemp /15-34 https://github.com/python/cpython/blob/46b16d0bdbb1722daed10389e27226a2370f1635/Lib/typing.py#L1786 +RedirectTemp /15-35 https://github.com/python/typeshed/blob/2a9f081abbf01134e4e04ced6a750107db904d70/stdlib/builtins.pyi#L239 +RedirectTemp /15-36 https://www.oreilly.com/library/view/robust-python/9781098100650/ +RedirectTemp /15-37 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +RedirectTemp /15-38 https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types +RedirectTemp /15-39 https://mypy.readthedocs.io/en/stable/common_issues.html#variance +RedirectTemp /15-40 https://www.artima.com/weblogs/viewpost.jsp?thread=85551 +RedirectTemp /15-41 https://dl.acm.org/action/cookieAbsent +RedirectTemp /15-42 http://bracha.org/pluggableTypesPosition.pdf +RedirectTemp /15-43 https://www.researchgate.net/publication/213886116_Static_Typing_Where_Possible_Dynamic_Typing_When_Needed_The_End_of_the_Cold_War_Between_Programming_Languages +RedirectTemp /15-44 https://www.atomickotlin.com/atomickotlin/ +RedirectTemp /15-45 https://www.informit.com/store/effective-java-9780134685991 +RedirectTemp /15-46 https://www.manning.com/books/programming-with-types +RedirectTemp /15-47 https://www.oreilly.com/library/view/programming-typescript/9781492037644/ +RedirectTemp /15-48 https://www.informit.com/store/dart-programming-language-9780321927705 +RedirectTemp /15-49 https://www.yodaiken.com/2017/09/15/bad-ideas-in-type-theory/ +RedirectTemp /15-50 https://www.yodaiken.com/2017/11/30/types-considered-harmful-ii/ +RedirectTemp /15-51 https://web.archive.org/web/20071010002142/http://weblogs.java.net/blog/arnold/archive/2005/06/generics_consid_1.html +RedirectTemp /15-52 https://github.com/python/cpython/blob/3e7ee02327db13e4337374597cdc4458ecb9e3ad/Lib/asyncio/trsock.py#L5 +RedirectTemp /15-53 https://www.python.org/dev/peps/pep-0484/#covariance-and-contravariance +############################################################ 16 +RedirectTemp /16-1 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-2 https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations +RedirectTemp /16-3 https://docs.python.org/3/reference/datamodel.html#object.__neg__ +RedirectTemp /16-4 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#boolean-indexing +RedirectTemp /16-5 https://docs.python.org/3/library/collections.html#collections.Counter +RedirectTemp /16-6 https://docs.python.org/3/reference/datamodel.html#emulating-container-types +RedirectTemp /16-7 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-8 https://www.fluentpython.com/lingo/#fail-fast +RedirectTemp /16-9 https://github.com/python/cpython/blob/0bbf30e2b910bc9c5899134ae9d73a8df968da35/Objects/typeobject.c#L4598 +RedirectTemp /16-10 https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html +RedirectTemp /16-11 https://treyhunner.com/2019/03/python-deep-comparisons-and-code-readability/ +RedirectTemp /16-12 https://docs.python.org/3/library/numbers.html#implementing-the-arithmetic-operations +RedirectTemp /16-13 https://docs.python.org/3/library/pathlib.html +RedirectTemp /16-14 https://pypi.org/project/scapy/ +RedirectTemp /16-15 https://scapy.readthedocs.io/en/latest/usage.html#stacking-layers +RedirectTemp /16-16 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /16-17 https://wiki.illinois.edu//wiki/download/attachments/273416327/ingalls.pdf +RedirectTemp /16-18 https://wiki.illinois.edu//wiki/download/attachments/273416327/double-dispatch.pdf +RedirectTemp /16-19 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-20 http://www.gotw.ca/publications/c_family_interview.htm +RedirectTemp /16-21 https://doc.rust-lang.org/std/ops/index.html +RedirectTemp /16-22 https://www.fluentpython.com/lingo/#lazy +############################################################ 17 +RedirectTemp /17-1 http://www.paulgraham.com/icad.html +RedirectTemp /17-2 https://en.wikipedia.org/wiki/Sentinel_value +RedirectTemp /17-3 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-4 https://docs.python.org/3.10/library/functions.html#iter +RedirectTemp /17-5 https://github.com/python/cpython/blob/b1930bf75f276cd7ca08c4455298128d89adf7d1/Lib/_collections_abc.py#L271 +RedirectTemp /17-6 https://github.com/python/cpython/blob/main/Lib/types.py#L6 +RedirectTemp /17-7 https://en.wikipedia.org/wiki/CLU_(programming_language) +RedirectTemp /17-8 https://docs.python.org/3/glossary.html +RedirectTemp /17-9 https://docs.python.org/3/glossary.html#term-generator-iterator +RedirectTemp /17-10 https://docs.python.org/3/glossary.html#term-generator-expression +RedirectTemp /17-11 https://marc.info/?l=python-list&m=141826925106951&w=2 +RedirectTemp /17-12 https://docs.python.org/3/library/os.html#os.walk +RedirectTemp /17-13 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-14 https://docs.python.org/3/library/exceptions.html#exception-hierarchy +RedirectTemp /17-15 https://en.wikipedia.org/wiki/Depth-first_search +RedirectTemp /17-16 https://docs.python.org/3.10/library/typing.html#typing.TypeAlias +RedirectTemp /17-17 https://docs.python.org/3/library/typing.html#typing.Generator +RedirectTemp /17-18 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-19 http://www.dabeaz.com/coroutines/Coroutines.pdf +RedirectTemp /17-20 https://mail.python.org/pipermail/python-ideas/2009-April/003841.html +RedirectTemp /17-21 https://mail.python.org/pipermail/python-ideas/2009-April/003912.html +RedirectTemp /17-22 https://docs.python.org/3/library/exceptions.html#StopIteration +RedirectTemp /17-23 https://docs.python.org/3/reference/expressions.html#yield-expressions +RedirectTemp /17-24 https://docs.python.org/3/reference/index.html +RedirectTemp /17-25 https://github.com/python/cpython/blob/6f743e7a4da904f61dfa84cc7d7385e4dcc79ac5/Lib/typing.py#L2060 +RedirectTemp /17-26 http://catb.org/~esr/jargon/html/G/grok.html +RedirectTemp /17-27 https://docs.python.org/3/reference/expressions.html#yieldexpr +RedirectTemp /17-28 https://docs.python.org/3/library/itertools.html +RedirectTemp /17-29 https://docs.python.org/3/library/itertools.html#itertools-recipes +RedirectTemp /17-30 https://more-itertools.readthedocs.io/en/stable/index.html +RedirectTemp /17-31 https://rittau.org/2006/11/java-iterators-are-not-iterable/ +RedirectTemp /17-32 https://docs.python.org/3/whatsnew/3.3.html#pep-380-syntax-for-delegating-to-a-subgenerator +RedirectTemp /17-33 http://www.dabeaz.com/generators/ +RedirectTemp /17-34 http://www.dabeaz.com/coroutines/ +RedirectTemp /17-35 https://archive.org/details/pyvideo_213___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-1-of-3 +RedirectTemp /17-36 https://archive.org/details/pyvideo_215___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-2-of-3 +RedirectTemp /17-37 https://archive.org/details/pyvideo_214___pycon-2009-a-curious-course-on-coroutines-and-concurrency-part-3-of-3 +RedirectTemp /17-38 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /17-39 https://web.archive.org/web/20200218150637/http://seriously.dontusethiscode.com/2013/05/01/greedy-coroutine.html +RedirectTemp /17-40 https://effectivepython.com/ +RedirectTemp /17-41 https://effectivepython.com/2015/03/10/consider-coroutines-to-run-many-functions-concurrently +RedirectTemp /17-42 https://en.wikipedia.org/wiki/Conway's_Game_of_Life +RedirectTemp /17-43 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-44 https://gist.github.com/ramalho/da5590bc38c973408839 +RedirectTemp /17-45 https://journal.code4lib.org/articles/4893 +RedirectTemp /17-46 https://github.com/fluentpython/isis2json +RedirectTemp /17-47 https://github.com/fluentpython/isis2json/blob/master/README.rst +############################################################ 18 +RedirectTemp /18-1 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-2 https://docs.python.org/3/library/sqlite3.html#using-the-connection-as-a-context-manager +RedirectTemp /18-3 https://docs.python.org/3/library/threading.html#using-locks-conditions-and-semaphores-in-the-with-statement +RedirectTemp /18-4 https://docs.python.org/3/library/decimal.html#decimal.localcontext +RedirectTemp /18-5 https://docs.python.org/3/library/unittest.mock.html#patch +RedirectTemp /18-6 https://docs.python.org/3/library/contextlib.html#contextlib.redirect_stdout +RedirectTemp /18-7 https://docs.python.org/3/library/sys.html#sys.exc_info +RedirectTemp /18-8 https://en.wikipedia.org/wiki/LL_parser +RedirectTemp /18-9 https://docs.python.org/3/library/contextlib.html +RedirectTemp /18-10 https://github.com/python/cpython/blob/8afab2ebbc1b343cd88d058914cf622fe687a2be/Lib/contextlib.py#L123 +RedirectTemp /18-11 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-12 https://docs.python.org/3/library/fileinput.html#fileinput.input +RedirectTemp /18-13 https://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/ +RedirectTemp /18-14 https://en.wikipedia.org/wiki/Euclidean_algorithm +RedirectTemp /18-15 https://github.com/fluentpython/example-code-2e/tree/master/18-with-match/lispy/py3.10/ +RedirectTemp /18-16 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-17 https://github.com/fluentpython/example-code-2e/blob/6527037ae7319ba370a1ee2d9fe79214d0ed9452/18-with-match/lispy/py3.10/lis.py#L35 +RedirectTemp /18-18 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-19 https://github.com/python/typeshed/issues/6042 +RedirectTemp /18-20 https://github.com/fluentpython/lispy/tree/main/mylis +RedirectTemp /18-21 https://github.com/fluentpython/example-code-2e/blob/00e4741926e1b771ee7c753148b1415c0bd12e39/02-array-seq/lispy/py3.10/examples_test.py +RedirectTemp /18-22 https://mitpress.mit.edu/sites/default/files/sicp/index.html +RedirectTemp /18-23 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/examples_test.py +RedirectTemp /18-24 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/py3.10/lis.py +RedirectTemp /18-25 https://www.python.org/dev/peps/pep-0634/#or-patterns +RedirectTemp /18-26 https://en.wikipedia.org/wiki/Lambda#Character_encodings +RedirectTemp /18-27 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-28 https://docs.python.org/3/glossary.html#term-eafp +RedirectTemp /18-29 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-30 https://docs.python.org/3/reference/compound_stmts.html +RedirectTemp /18-31 https://stackoverflow.com/questions/16138232/is-it-a-good-practice-to-use-try-except-else-in-python +RedirectTemp /18-32 https://docs.python.org/3/library/stdtypes.html#typecontextmanager +RedirectTemp /18-33 https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers +RedirectTemp /18-34 https://speakerdeck.com/pyconslides/pycon-keynote-python-is-awesome-by-raymond-hettinger?slide=21 +RedirectTemp /18-35 https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1?slide=34 +RedirectTemp /18-36 https://preshing.com/20110920/the-python-with-statement-by-example/ +RedirectTemp /18-37 https://www.rath.org/on-the-beauty-of-pythons-exitstack.html +RedirectTemp /18-38 https://norvig.com/lispy.html +RedirectTemp /18-39 https://norvig.com/lispy2.html +RedirectTemp /18-40 https://github.com/norvig/pytudes +RedirectTemp /18-41 https://github.com/fluentpython/lispy +RedirectTemp /18-42 https://racket-lang.org/ +RedirectTemp /18-43 https://pyvideo.org/video/1669/keynote-3/ +RedirectTemp /18-44 https://en.wikipedia.org/wiki/Tail_call +RedirectTemp /18-45 https://2ality.com/2015/06/tail-call-optimization.html +RedirectTemp /18-46 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lis.py +RedirectTemp /18-47 https://github.com/fluentpython/example-code-2e/blob/master/18-with-match/lispy/original/lispy.py +RedirectTemp /18-48 http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html +RedirectTemp /18-49 https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ +RedirectTemp /18-50 http://kangax.github.io/compat-table/es6/ +RedirectTemp /18-51 https://world.hey.com/mgmarlow/what-happened-to-proper-tail-calls-in-javascript-5494c256 +RedirectTemp /18-52 http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html +RedirectTemp /18-53 https://github.com/fluentpython/lispy/blob/main/mylis/mylis_2/lis.py +RedirectTemp /18-54 https://github.com/fluentpython/lispy/tree/main/mylis +############################################################ 19 +RedirectTemp /19-1 https://go.dev/blog/waza-talk +RedirectTemp /19-2 https://en.wikipedia.org/wiki/Graphics_processing_unit +RedirectTemp /19-3 https://docs.python.org/3/library/sys.html#sys.getswitchinterval +RedirectTemp /19-4 https://docs.python.org/3/library/sys.html#sys.setswitchinterval +RedirectTemp /19-5 https://en.wikipedia.org/wiki/System_call +RedirectTemp /19-6 https://mail.python.org/pipermail/python-dev/2009-October/093356.html +RedirectTemp /19-7 http://www.dabeaz.com/finalgenerator/ +RedirectTemp /19-8 https://docs.python.org/3/library/threading.html#thread-objects +RedirectTemp /19-9 https://www.pypy.org/ +RedirectTemp /19-10 https://mail.python.org/pipermail/python-list/2009-February/675659.html +RedirectTemp /19-11 https://en.wikipedia.org/wiki/Braille_Patterns +RedirectTemp /19-12 https://docs.python.org/3/library/multiprocessing.shared_memory.html +RedirectTemp /19-13 https://docs.python.org/3/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.ShareableList +RedirectTemp /19-14 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-15 https://docs.sqlalchemy.org/en/14/changelog/migration_14.html#asynchronous-io-support-for-core-and-orm +RedirectTemp /19-16 https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html +RedirectTemp /19-17 http://www.gevent.org/ +RedirectTemp /19-18 https://github.com/gevent/gevent/wiki/Projects +RedirectTemp /19-19 https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example +RedirectTemp /19-20 https://github.com/python/asyncio/issues/284 +RedirectTemp /19-21 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /19-22 https://mail.python.org/archives/list/python-dev@python.org/message/JBYXQH3NV3YBF7P2HLHB5CD6V3GVTY55/ +RedirectTemp /19-23 https://docs.python.org/3/library/queue.html#queue.SimpleQueue.get +RedirectTemp /19-24 https://en.wikipedia.org/wiki/Race_condition +RedirectTemp /19-25 https://github.com/fluentpython/example-code-2e/commit/2c1230579db99738a5e5e6802063bda585f6476d +RedirectTemp /19-26 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/README.md +RedirectTemp /19-27 https://github.com/fluentpython/example-code-2e/blob/master/19-concurrency/primes/threads.py +RedirectTemp /19-28 https://en.wikipedia.org/wiki/Context_switch +RedirectTemp /19-29 http://www.gotw.ca/publications/concurrency-ddj.htm +RedirectTemp /19-30 https://www.ansible.com/ +RedirectTemp /19-31 https://saltproject.io/ +RedirectTemp /19-32 https://www.fabfile.org/ +RedirectTemp /19-33 https://engineering.fb.com/2016/05/27/production-engineering/python-in-production-engineering/ +RedirectTemp /19-34 https://jupyter.org/ +RedirectTemp /19-35 https://docs.bokeh.org/en/latest/index.html +RedirectTemp /19-36 https://www.tensorflow.org/ +RedirectTemp /19-37 https://pytorch.org/ +RedirectTemp /19-38 https://www.oreilly.com/radar/where-programming-ops-ai-and-the-cloud-are-headed-in-2021/ +RedirectTemp /19-39 https://www.youtube.com/watch?v=ods97a5Pzw0 +RedirectTemp /19-40 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-41 https://modwsgi.readthedocs.io/en/master/ +RedirectTemp /19-42 https://uwsgi-docs.readthedocs.io/en/latest/ +RedirectTemp /19-43 https://unit.nginx.org/ +RedirectTemp /19-44 https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/ +RedirectTemp /19-45 https://www.youtube.com/watch?v=p6R1h2Nn468 +RedirectTemp /19-46 https://asgi.readthedocs.io/en/latest/index.html +RedirectTemp /19-47 https://docs.celeryproject.org/en/stable/getting-started/introduction.html +RedirectTemp /19-48 https://python-rq.org/ +RedirectTemp /19-49 https://docs.celeryproject.org/en/stable/faq.html#what-kinds-of-things-should-i-use-celery-for +RedirectTemp /19-50 https://redis.io/ +RedirectTemp /19-51 https://realpython.com/intro-to-python-threading/ +RedirectTemp /19-52 https://pymotw.com/3/concurrency.html +RedirectTemp /19-53 https://www.pearson.com/us/higher-education/program/Hellmann-Python-3-Standard-Library-by-Example-The/PGM328871.html +RedirectTemp /19-54 https://docs.python.org/3/library/multiprocessing.html#programming-guidelines +RedirectTemp /19-55 https://docs.python.org/3/library/multiprocessing.html +RedirectTemp /19-56 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-57 https://link.springer.com/book/10.1007/978-1-4842-5793-7?error=cookies_not_supported&code=2ed5d61d-ae9f-4f3d-94ac-0f68cf45ea4f +RedirectTemp /19-58 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-59 https://greenteapress.com/wp/semaphores/ +RedirectTemp /19-60 https://docs.python.org/3/c-api/init.html#thread-state-and-the-global-interpreter-lock +RedirectTemp /19-61 https://docs.python.org/3/faq/library.html#can-t-we-get-rid-of-the-global-interpreter-lock +RedirectTemp /19-62 https://www.artima.com/weblogs/viewpost.jsp?thread=214235 +RedirectTemp /19-63 http://jessenoller.com/blog/2009/02/01/python-threads-and-the-global-interpreter-lock +RedirectTemp /19-64 https://realpython.com/products/cpython-internals-book/ +RedirectTemp /19-65 http://www.dabeaz.com/GIL/ +RedirectTemp /19-66 http://www.dabeaz.com/python/UnderstandingGIL.pdf +RedirectTemp /19-67 https://bugs.python.org/issue7946#msg223110 +RedirectTemp /19-68 https://bugs.python.org/issue7946 +RedirectTemp /19-69 https://www.fullstackpython.com/ +RedirectTemp /19-70 https://www.oreilly.com/library/view/high-performance-python/9781492055013/ +RedirectTemp /19-71 https://www.packtpub.com/product/parallel-programming-with-python/9781783288397 +RedirectTemp /19-72 https://www.packtpub.com/product/distributed-computing-with-python/9781785889691 +RedirectTemp /19-73 https://towardsdatascience.com/python-performance-and-gpus-1be860ffd58d?gi=a7d537cc2fb4 +RedirectTemp /19-74 https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366?gi=12a441991c88 +RedirectTemp /19-75 https://www.oreilly.com/library/view/architecture-patterns-with/9781492052197/ +RedirectTemp /19-76 https://www.cosmicpython.com/ +RedirectTemp /19-77 https://pypi.org/project/lelo/ +RedirectTemp /19-78 https://github.com/npryce/python-parallelize +RedirectTemp /19-79 https://github.com/ericsnowcurrently/multi-core-python/wiki +RedirectTemp /19-80 https://gist.github.com/markshannon/79cace3656b40e21b7021504daee950c +RedirectTemp /19-81 https://mail.python.org/archives/list/python-dev@python.org/message/YOOQZCFOKEPQ24YHWWLQSJ3RCXFMS7D7/ +RedirectTemp /19-82 https://en.wikipedia.org/wiki/Communicating_sequential_processes +RedirectTemp /19-83 https://github.com/stackless-dev/stackless/wiki +RedirectTemp /19-84 https://www.eveonline.com +RedirectTemp /19-85 https://www.ccpgames.com/ +RedirectTemp /19-86 https://stackless.readthedocs.io/en/3.6-slp/stackless-python.html#history +RedirectTemp /19-87 https://doc.pypy.org/en/latest/stackless.html +RedirectTemp /19-88 https://greenlet.readthedocs.io/en/latest/ +RedirectTemp /19-89 http://www.gevent.org/ +RedirectTemp /19-90 http://thespianpy.com/doc/ +RedirectTemp /19-91 https://pykka.readthedocs.io/en/latest/ +RedirectTemp /19-92 https://www.manning.com/books/rabbitmq-in-action +RedirectTemp /19-93 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /19-94 https://en.wikipedia.org/wiki/OpenCL +RedirectTemp /19-95 https://media.pragprog.com/titles/pb7con/Bonus_Chapter.pdf +RedirectTemp /19-96 https://martinfowler.com/ +RedirectTemp /19-97 https://martinfowler.com/articles/patterns-of-distributed-systems/ +RedirectTemp /19-98 https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/ +RedirectTemp /19-99 https://www.oreilly.com/library/view/oscon-2016-video/9781491965153/video247021.html +RedirectTemp /19-100 https://www.oreilly.com/library/view/designing-for-scalability/9781449361556/ +RedirectTemp /19-101 https://www.thoughtworks.com/radar/techniques/high-performance-envy-web-scale-envy +RedirectTemp /19-102 https://en.wikipedia.org/wiki/KISS_principle +RedirectTemp /19-103 https://www.usenix.org/conference/hotos15/workshop-program/presentation/mcsherry +############################################################ 20 +RedirectTemp /20-1 https://www.artima.com/weblogs/viewpost.jsp?thread=299551 +RedirectTemp /20-2 https://docs.python.org/3/library/http.server.html +RedirectTemp /20-3 https://www.youtube.com/watch?v=A9e9Cy1UkME +RedirectTemp /20-4 https://www.cia.gov/the-world-factbook/ +RedirectTemp /20-5 https://docs.python-requests.org/en/latest/ +RedirectTemp /20-6 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor +RedirectTemp /20-7 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-8 https://docs.python.org/3/library/concurrent.futures.html +RedirectTemp /20-9 https://docs.python.org/3.10/library/concurrent.futures.html#concurrent.futures.Executor +RedirectTemp /20-10 https://github.com/fluentpython/example-code-2e/blob/master/20-executors/getflags/flags2_common.py +RedirectTemp /20-11 https://github.com/noamraph/tqdm +RedirectTemp /20-12 https://www.youtube.com/watch?v=M8Z65tAl5l4 +RedirectTemp /20-13 https://github.com/noamraph/tqdm/blob/master/README.md +RedirectTemp /20-14 https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.as_completed +RedirectTemp /20-15 https://docs.python.org/3/library/asyncio-task.html#asyncio.as_completed +RedirectTemp /20-16 https://www.cloudflare.com/ +RedirectTemp /20-17 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /20-18 https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/418 +RedirectTemp /20-19 https://en.wikipedia.org/wiki/Embarrassingly_parallel +RedirectTemp /20-20 https://pyvideo.org/video/480/pyconau-2010--the-future-is-soon/ +RedirectTemp /20-21 http://www.dabeaz.com/coroutines/ +RedirectTemp /20-22 https://en.wikipedia.org/wiki/POSIX_Threads +RedirectTemp /20-23 https://en.wikipedia.org/wiki/C_dynamic_memory_allocation +RedirectTemp /20-24 https://pragprog.com/titles/pb7con/seven-concurrency-models-in-seven-weeks/ +RedirectTemp /20-25 https://hexdocs.pm/ecto/getting-started.html +############################################################ 21 +RedirectTemp /21-1 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-2 https://bugs.python.org/issue43216 +RedirectTemp /21-3 https://bugs.python.org/issue36921 +RedirectTemp /21-4 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.getaddrinfo +RedirectTemp /21-5 https://docs.python.org/3/library/socket.html#socket.getaddrinfo +RedirectTemp /21-6 https://docs.python.org/3.10/library/asyncio-eventloop.html#asyncio.get_event_loop +RedirectTemp /21-7 https://www.python.org/dev/peps/pep-0492/#await-expression +RedirectTemp /21-8 https://www.fluentpython.com/extra/classic-coroutines/#yield_from_meaning_sec +RedirectTemp /21-9 https://github.com/fluentpython/example-code-2e/tree/master/20-executors/getflags +RedirectTemp /21-10 https://magicstack.github.io/asyncpg/current/ +RedirectTemp /21-11 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-12 https://magicstack.github.io/asyncpg/current/api/index.html#transactions +RedirectTemp /21-13 https://github.com/MagicStack/asyncpg/blob/4d39a05268ce4cc01b00458223a767542da048b8/asyncpg/transaction.py#L57 +RedirectTemp /21-14 https://magicstack.github.io/asyncpg/current/usage.html#connection-pools +RedirectTemp /21-15 https://gist.github.com/jboner/2841832 +RedirectTemp /21-16 https://en.wikipedia.org/wiki/Network-attached_storage +RedirectTemp /21-17 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-18 https://en.wikipedia.org/wiki/Semaphore_(programming) +RedirectTemp /21-19 https://groups.google.com/forum/#!msg/python-tulip/PdAEtwpaJHs/7fqb-Qj2zJoJ +RedirectTemp /21-20 https://web.archive.org/web/20151209151711/http://tritarget.org/blog/2012/11/28/the-pyramid-of-doom-a-javascript-style-trap +RedirectTemp /21-21 https://stackoverflow.com/questions/53701841/what-is-the-use-case-for-future-add-done-callback/53710563 +RedirectTemp /21-22 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor +RedirectTemp /21-23 https://motor.readthedocs.io/en/stable/ +RedirectTemp /21-24 https://emptysqua.re/blog/response-to-asynchronous-python-and-databases/ +RedirectTemp /21-25 https://docs.python.org/3/library/asyncio-stream.html#tcp-echo-server-using-streams +RedirectTemp /21-26 https://en.wikipedia.org/wiki/Phaistos_Disc +RedirectTemp /21-27 https://en.wikipedia.org/wiki/Inverted_index +RedirectTemp /21-28 https://fastapi.tiangolo.com/ +RedirectTemp /21-29 https://swagger.io/specification/ +RedirectTemp /21-30 https://asgi.readthedocs.io/en/latest/implementations.html +RedirectTemp /21-31 https://pydantic-docs.helpmanual.io/ +RedirectTemp /21-32 https://doc.traefik.io/traefik/ +RedirectTemp /21-33 https://fastapi.tiangolo.com/project-generation/ +RedirectTemp /21-34 https://fastapi.tiangolo.com/tutorial/response-model/ +RedirectTemp /21-35 https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server +RedirectTemp /21-36 https://github.com/python/typeshed/issues/5535 +RedirectTemp /21-37 https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Server.serve_forever +RedirectTemp /21-38 https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.close +RedirectTemp /21-39 https://docs.python.org/3/library/asyncio-stream.html#streamwriter +RedirectTemp /21-40 https://docs.python.org/3/library/asyncio-stream.html +RedirectTemp /21-41 https://docs.python.org/3/library/asyncio-protocol.html +RedirectTemp /21-42 https://docs.python.org/3/library/asyncio-protocol.html#tcp-echo-server +RedirectTemp /21-43 https://github.com/aio-libs/aiopg +RedirectTemp /21-44 https://docs.python.org/3/whatsnew/3.8.html#asyncio +RedirectTemp /21-45 https://datatracker.ietf.org/doc/html/rfc6761 +RedirectTemp /21-46 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-47 https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager +RedirectTemp /21-48 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather +RedirectTemp /21-49 https://curio.readthedocs.io/en/latest/index.html +RedirectTemp /21-50 https://curio.readthedocs.io/en/latest/reference.html#task-groups +RedirectTemp /21-51 https://en.wikipedia.org/wiki/Structured_concurrency +RedirectTemp /21-52 https://mail.python.org/archives/list/python-dev@python.org/thread/2ORDAW74LGE3ZI2QETPJRT2ZL7MCCPG2/ +RedirectTemp /21-53 https://www.python.org/dev/peps/pep-0654/#motivation +RedirectTemp /21-54 https://curio.readthedocs.io/en/latest/reference.html#AWAIT +RedirectTemp /21-55 https://www.python-httpx.org/async/#curio +RedirectTemp /21-56 https://github.com/dabeaz/curio/tree/78bca8a6ad677ef51e1568ac7b3e51441ab49c42/examples +RedirectTemp /21-57 https://datatracker.ietf.org/doc/html/rfc8305 +RedirectTemp /21-58 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-59 https://www.youtube.com/watch?v=M-sc73Y-zQA +RedirectTemp /21-60 https://en.wikipedia.org/wiki/Technical_debt +RedirectTemp /21-61 https://www.youtube.com/watch?v=E-1Y4kSsAFc +RedirectTemp /21-62 https://github.com/dabeaz/curio +RedirectTemp /21-63 https://trio.readthedocs.io/en/stable/ +RedirectTemp /21-64 https://curio.readthedocs.io/en/latest/#curio-university +RedirectTemp /21-65 https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/ +RedirectTemp /21-66 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-67 https://stackoverflow.com/questions/49482969/what-is-the-core-difference-between-asyncio-and-trio +RedirectTemp /21-68 https://docs.python.org/3/library/asyncio.html +RedirectTemp /21-69 https://bugs.python.org/issue33649 +RedirectTemp /21-70 https://docs.python.org/3/library/asyncio-dev.html +RedirectTemp /21-71 https://www.youtube.com/watch?v=iG6fr81xHKA +RedirectTemp /21-72 https://www.youtube.com/watch?v=F19R_M4Nay4 +RedirectTemp /21-73 https://asherman.io/projects/unsync.html +RedirectTemp /21-74 https://pyladies.com/ +RedirectTemp /21-75 https://www.youtube.com/watch?v=sW76-pRkZk8 +RedirectTemp /21-76 https://www.youtube.com/watch?v=Xbl7XjFYsN4 +RedirectTemp /21-77 https://www.youtube.com/watch?v=02CLD-42VdI +RedirectTemp /21-78 https://micropython.org/ +RedirectTemp /21-79 https://docs.micropython.org/en/latest/library/uasyncio.html +RedirectTemp /21-80 https://www.encode.io/articles/python-async-frameworks-beyond-developer-tribalism +RedirectTemp /21-81 https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ +RedirectTemp /21-82 https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ +RedirectTemp /21-83 https://github.com/MagicStack/uvloop +RedirectTemp /21-84 http://magic.io/blog/uvloop-blazing-fast-python-networking/ +RedirectTemp /21-85 https://github.com/MagicStack/httptools +RedirectTemp /21-86 https://docs.aiohttp.org/en/stable/ +RedirectTemp /21-87 https://github.com/wg/wrk +RedirectTemp /21-88 https://twistedmatrix.com/trac/ +############################################################ 22 +RedirectTemp /22-1 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/data/osconfeed.json +RedirectTemp /22-2 https://pypi.org/project/attrdict/ +RedirectTemp /22-3 https://pypi.org/project/addict/ +RedirectTemp /22-4 https://github.com/ActiveState/code/tree/master/recipes/Python/52308_simple_but_handy_collector_bunch_named_stuff +RedirectTemp /22-5 https://docs.python.org/3/library/types.html#types.SimpleNamespace +RedirectTemp /22-6 https://docs.python.org/3/library/argparse.html#argparse.Namespace +RedirectTemp /22-7 https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.Namespace +RedirectTemp /22-8 https://github.com/fluentpython/example-code-2e/blob/master/22-dyn-attr-prop/oscon/schedule_v2.py +RedirectTemp /22-9 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-10 https://docs.python.org/3/library/functools.html#functools.cached_property +RedirectTemp /22-11 https://bugs.python.org/issue42781 +RedirectTemp /22-12 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /22-13 https://github.com/python/cpython/blob/e6d0107e13ed957109e79b796984d3d026a8660d/Lib/functools.py#L926 +RedirectTemp /22-14 https://docs.python.org/3/library/threading.html#rlock-objects +RedirectTemp /22-15 https://docs.python.org/3.10/library/functools.html#functools.cached_property +RedirectTemp /22-16 https://www.wsj.com/articles/SB10001424052970203914304576627102996831200 +RedirectTemp /22-17 https://www.youtube.com/watch?v=s35rVw1zskA&feature=youtu.be +RedirectTemp /22-18 https://docs.python.org/3/library/functions.html#dir +RedirectTemp /22-19 https://github.com/python/cpython/blob/19903085c3ad7a17c8047e1556c700f2eb109931/Lib/cmd.py#L214 +RedirectTemp /22-20 https://docs.python.org/3/library/functions.html#hasattr +RedirectTemp /22-21 https://docs.python.org/3.10/reference/datamodel.html#special-method-lookup +RedirectTemp /22-22 https://docs.python.org/3/library/functions.html +RedirectTemp /22-23 https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access +RedirectTemp /22-24 https://docs.python.org/3/reference/datamodel.html#special-method-lookup +RedirectTemp /22-25 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /22-26 http://wiki.c2.com/?WelcomeVisitors +RedirectTemp /22-27 http://wiki.c2.com/?UniformAccessPrinciple +RedirectTemp /22-28 https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html +RedirectTemp /22-29 http://www.pingo.io/docs/ +RedirectTemp /22-30 https://www.drdobbs.com/javas-new-considered-harmful/184405016 +RedirectTemp /22-31 https://www.python.org/dev/peps/pep-0008/#class-names +############################################################ 23 +RedirectTemp /23-1 http://www.aleax.it/goo_pydp.pdf +RedirectTemp /23-2 https://docs.python.org/3.10/reference/datamodel.html#implementing-descriptors +RedirectTemp /23-3 https://docs.python.org/3/howto/descriptor.html +RedirectTemp /23-4 https://docs.python.org/3/howto/ +RedirectTemp /23-5 http://www.aleax.it/Python/nylug05_om.pdf +RedirectTemp /23-6 https://www.youtube.com/watch?v=VOzvpHoYQoo +RedirectTemp /23-7 https://www.python.org/dev/peps/pep-0487/#trait-descriptors +RedirectTemp /23-8 https://dreamsongs.com/RiseOfWorseIsBetter.html +RedirectTemp /23-9 http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html +RedirectTemp /23-10 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this +RedirectTemp /23-11 http://python-history.blogspot.com/2009/02/adding-support-for-user-defined-classes.html +############################################################ 24 +RedirectTemp /24-1 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-2 https://docs.djangoproject.com/en/3.2/topics/db/models/#meta-options +RedirectTemp /24-3 https://www.python.org/dev/peps/pep-3155/ +RedirectTemp /24-4 https://github.com/python/cpython/blob/3.9/Lib/collections/__init__.py +RedirectTemp /24-5 https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions +RedirectTemp /24-6 https://go.dev/tour/basics/12 +RedirectTemp /24-7 https://bugs.python.org/issue42102 +RedirectTemp /24-8 https://docs.python.org/3/reference/datamodel.html#object.__init_subclass__ +RedirectTemp /24-9 https://www.python.org/dev/peps/pep-0557/#abstract +RedirectTemp /24-10 https://github.com/python/cpython/blob/3.9/Lib/dataclasses.py +RedirectTemp /24-11 https://docs.python.org/3/reference/datamodel.html#creating-the-class-object +RedirectTemp /24-12 https://mail.python.org/pipermail/python-list/2002-December/134521.html +RedirectTemp /24-13 https://mail.python.org/pipermail/python-list/2002-July/162558.html +RedirectTemp /24-14 https://github.com/fluentpython/example-code/tree/master/21-class-metaprog/bulkfood +RedirectTemp /24-15 https://en.wikipedia.org/wiki/Principle_of_least_astonishment +RedirectTemp /24-16 https://docs.python.org/3/reference/datamodel.html#object.__class_getitem__ +RedirectTemp /24-17 https://en.wikipedia.org/wiki/Trait_(computer_programming) +RedirectTemp /24-18 https://en.wikipedia.org/wiki/Aspect-oriented_programming +RedirectTemp /24-19 https://dhh.dk/arc/000416.html +RedirectTemp /24-20 https://github.com/cjrh/autoslot +RedirectTemp /24-21 https://docs.python.org/3/reference/datamodel.html#customizing-class-creation +RedirectTemp /24-22 https://docs.python.org/3/library/functions.html#type +RedirectTemp /24-23 https://docs.python.org/3/library/stdtypes.html#special-attributes +RedirectTemp /24-24 https://docs.python.org/3/library/types.html +RedirectTemp /24-25 https://www.python.org/dev/peps/pep-3129/ +RedirectTemp /24-26 https://www.youtube.com/watch?v=cAGliEJV9_o +RedirectTemp /24-27 https://docs.python.org/3/library/functools.html#functools.total_ordering +RedirectTemp /24-28 https://www.python.org/download/releases/2.2.3/descrintro/ +RedirectTemp /24-29 https://github.com/lihaoyi/macropy +RedirectTemp /24-30 https://people.eecs.berkeley.edu/~bh/ss-toc2.html +# content of short.htaccess file created and managed by short.py + +# appended: 2025-05-23 15:12:13 +RedirectTemp /22 https://pythonfluente.com/2/#pattern_matching_case_study_sec +RedirectTemp /23 https://pythonfluente.com/2/#how_slicing_works +RedirectTemp /24 https://pythonfluente.com/2/#sliceable_sequence +RedirectTemp /25 https://pythonfluente.com/2/#virtual_subclass_sec +RedirectTemp /26 https://pythonfluente.com/2/#environment_class_ex +RedirectTemp /27 https://pythonfluente.com/2/#subclass_builtin_woes +RedirectTemp /28 https://pythonfluente.com/2/#slots_section +RedirectTemp /29 https://pythonfluente.com/2/#typeddict_sec +RedirectTemp /2a https://pythonfluente.com/2/#problems_annot_runtime_sec +RedirectTemp /2b https://pythonfluente.com/2/#legacy_deprecated_typing_box +RedirectTemp /2c https://pythonfluente.com/2/#positional_pattern_implement_sec diff --git a/links/README.md b/links/README.md new file mode 100644 index 0000000..65749b2 --- /dev/null +++ b/links/README.md @@ -0,0 +1,6 @@ +This file is deployed as `.htaccess` to the FPY.LI domain +to map short URLs in Fluent Python to the original URLs. + +To update it, I use tools in this other repo: + +https://github.com/pythonfluente/pythonfluente2e diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..f7b07e2 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,4 @@ +line-length = 100 +[format] +# Like Python's repr(), use single quotes for strings. +quote-style = "single" \ No newline at end of file