diff --git a/12_functions_advanced.ipynb b/12_functions_advanced.ipynb index 3950c49b..42d4a39d 100644 --- a/12_functions_advanced.ipynb +++ b/12_functions_advanced.ipynb @@ -56,9 +56,7 @@ { "cell_type": "markdown", "id": "2", - "metadata": { - "jp-MarkdownHeadingCollapsed": true - }, + "metadata": {}, "source": [ "## Recap" ] @@ -295,7 +293,9 @@ { "cell_type": "markdown", "id": "19", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "## Mutable objects as default values of function's parameters" ] @@ -745,7 +745,9 @@ { "cell_type": "markdown", "id": "57", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "## Lambdas" ] @@ -2005,7 +2007,7 @@ "metadata": {}, "outputs": [], "source": [ - "f = incrementer(2)\n", + "f = incrementer(n=2)\n", "f.__code__.co_freevars" ] }, @@ -2043,14 +2045,16 @@ "metadata": {}, "outputs": [], "source": [ - "inc_10 = incrementer(10)(100)\n", + "inc_10 = incrementer(n=10)(start=100)\n", "inc_10()" ] }, { "cell_type": "markdown", "id": "177", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "### Quiz: Closures" ] @@ -2072,7 +2076,9 @@ { "cell_type": "markdown", "id": "179", - "metadata": {}, + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "### Closures: examples" ] @@ -2511,7 +2517,7 @@ }, "outputs": [], "source": [ - "def fcounter(function):\n", + "def function_counter(function):\n", " count = 0\n", "\n", " def inner(*args, **kwargs):\n", @@ -2556,7 +2562,7 @@ "metadata": {}, "outputs": [], "source": [ - "counter_fact = fcounter(factorial)" + "counter_fact = function_counter(factorial)" ] }, { @@ -2627,7 +2633,7 @@ "metadata": {}, "outputs": [], "source": [ - "@fcounter\n", + "@function_counter\n", "def mult(a: float, b: float) -> float:\n", " \"\"\"Multiplies two floats\"\"\"\n", " return a * b" @@ -2895,13 +2901,21 @@ "id": "250", "metadata": {}, "source": [ - "#### Example 1: timer" + "Check out also the notebook in the `extra/` folder: [Decorators example: retry & validate](./extra/12a_decorators_retry_validate.ipynb) for two examples of usage patterns built from scratch." ] }, { "cell_type": "markdown", "id": "251", "metadata": {}, + "source": [ + "#### Example 1: timer" + ] + }, + { + "cell_type": "markdown", + "id": "252", + "metadata": {}, "source": [ "This is classic example of using decorators: creating a timer for a generic function." ] @@ -2909,7 +2923,7 @@ { "cell_type": "code", "execution_count": null, - "id": "252", + "id": "253", "metadata": { "lines_to_next_cell": 2 }, @@ -2943,7 +2957,7 @@ }, { "cell_type": "markdown", - "id": "253", + "id": "254", "metadata": {}, "source": [ "Let's test it with a function to calculate the n-th Fibonacci number: `1, 1, 2, 3, 5, 8, 11, ...`\n", @@ -2959,7 +2973,7 @@ }, { "cell_type": "markdown", - "id": "254", + "id": "255", "metadata": { "lines_to_next_cell": 2 }, @@ -2970,7 +2984,7 @@ { "cell_type": "code", "execution_count": null, - "id": "255", + "id": "256", "metadata": {}, "outputs": [], "source": [ @@ -2983,7 +2997,7 @@ { "cell_type": "code", "execution_count": null, - "id": "256", + "id": "257", "metadata": {}, "outputs": [], "source": [ @@ -2993,7 +3007,7 @@ { "cell_type": "code", "execution_count": null, - "id": "257", + "id": "258", "metadata": {}, "outputs": [], "source": [ @@ -3003,7 +3017,7 @@ { "cell_type": "code", "execution_count": null, - "id": "258", + "id": "259", "metadata": {}, "outputs": [], "source": [ @@ -3015,7 +3029,7 @@ { "cell_type": "code", "execution_count": null, - "id": "259", + "id": "260", "metadata": {}, "outputs": [], "source": [ @@ -3025,7 +3039,7 @@ { "cell_type": "code", "execution_count": null, - "id": "260", + "id": "261", "metadata": {}, "outputs": [], "source": [ @@ -3035,7 +3049,7 @@ { "cell_type": "code", "execution_count": null, - "id": "261", + "id": "262", "metadata": { "lines_to_next_cell": 2 }, @@ -3046,7 +3060,7 @@ }, { "cell_type": "markdown", - "id": "262", + "id": "263", "metadata": {}, "source": [ "Sounds a bit long, doesn't it?\n", @@ -3056,7 +3070,7 @@ }, { "cell_type": "markdown", - "id": "263", + "id": "264", "metadata": { "lines_to_next_cell": 2 }, @@ -3067,7 +3081,7 @@ { "cell_type": "code", "execution_count": null, - "id": "264", + "id": "265", "metadata": {}, "outputs": [], "source": [ @@ -3085,7 +3099,7 @@ { "cell_type": "code", "execution_count": null, - "id": "265", + "id": "266", "metadata": {}, "outputs": [], "source": [ @@ -3095,7 +3109,7 @@ }, { "cell_type": "markdown", - "id": "266", + "id": "267", "metadata": {}, "source": [ "Incredibly more efficient!\n", @@ -3104,7 +3118,7 @@ }, { "cell_type": "markdown", - "id": "267", + "id": "268", "metadata": {}, "source": [ "##### Fibonacci using `reduce`" @@ -3112,7 +3126,7 @@ }, { "cell_type": "markdown", - "id": "268", + "id": "269", "metadata": {}, "source": [ "First, a quick refresher:" @@ -3121,7 +3135,7 @@ { "cell_type": "code", "execution_count": null, - "id": "269", + "id": "270", "metadata": {}, "outputs": [], "source": [ @@ -3131,7 +3145,7 @@ { "cell_type": "code", "execution_count": null, - "id": "270", + "id": "271", "metadata": { "lines_to_next_cell": 2 }, @@ -3142,7 +3156,7 @@ }, { "cell_type": "markdown", - "id": "271", + "id": "272", "metadata": {}, "source": [ "It's just the progressive sum of pairs of numbers.\n", @@ -3155,7 +3169,7 @@ }, { "cell_type": "markdown", - "id": "272", + "id": "273", "metadata": { "lines_to_next_cell": 2 }, @@ -3190,7 +3204,7 @@ { "cell_type": "code", "execution_count": null, - "id": "273", + "id": "274", "metadata": {}, "outputs": [], "source": [ @@ -3204,7 +3218,7 @@ { "cell_type": "code", "execution_count": null, - "id": "274", + "id": "275", "metadata": {}, "outputs": [], "source": [ @@ -3214,7 +3228,7 @@ }, { "cell_type": "markdown", - "id": "275", + "id": "276", "metadata": {}, "source": [ "If we compare the three methods:" @@ -3223,7 +3237,7 @@ { "cell_type": "code", "execution_count": null, - "id": "276", + "id": "277", "metadata": { "lines_to_next_cell": 2 }, @@ -3236,7 +3250,7 @@ }, { "cell_type": "markdown", - "id": "277", + "id": "278", "metadata": {}, "source": [ "Although the recursive method is the __easiest__ to understand, it's also the slowest because it's written inefficiently.\n", @@ -3245,7 +3259,7 @@ }, { "cell_type": "markdown", - "id": "278", + "id": "279", "metadata": {}, "source": [ "#### Example 2: memoization" @@ -3253,7 +3267,7 @@ }, { "cell_type": "markdown", - "id": "279", + "id": "280", "metadata": {}, "source": [ "The previous example showed one task that a decorator can accomplish pretty well: adding some feature to a predefined function.\n", @@ -3262,7 +3276,7 @@ }, { "cell_type": "markdown", - "id": "280", + "id": "281", "metadata": { "lines_to_next_cell": 2 }, @@ -3274,7 +3288,7 @@ { "cell_type": "code", "execution_count": null, - "id": "281", + "id": "282", "metadata": {}, "outputs": [], "source": [ @@ -3286,7 +3300,7 @@ { "cell_type": "code", "execution_count": null, - "id": "282", + "id": "283", "metadata": { "lines_to_next_cell": 2 }, @@ -3297,7 +3311,7 @@ }, { "cell_type": "markdown", - "id": "283", + "id": "284", "metadata": {}, "source": [ "You can see that `fib(2)` is calculated **three times**.\n", @@ -3307,7 +3321,7 @@ }, { "cell_type": "markdown", - "id": "284", + "id": "285", "metadata": {}, "source": [ "We'll see how we can improve this approach using a decorator and a caching mechanism for previously calculated numbers.\n", @@ -3316,7 +3330,7 @@ }, { "cell_type": "markdown", - "id": "285", + "id": "286", "metadata": { "lines_to_next_cell": 2 }, @@ -3327,7 +3341,7 @@ { "cell_type": "code", "execution_count": null, - "id": "286", + "id": "287", "metadata": {}, "outputs": [], "source": [ @@ -3345,7 +3359,7 @@ { "cell_type": "code", "execution_count": null, - "id": "287", + "id": "288", "metadata": {}, "outputs": [], "source": [ @@ -3356,7 +3370,7 @@ { "cell_type": "code", "execution_count": null, - "id": "288", + "id": "289", "metadata": { "lines_to_next_cell": 2 }, @@ -3367,7 +3381,7 @@ }, { "cell_type": "markdown", - "id": "289", + "id": "290", "metadata": { "lines_to_next_cell": 2 }, @@ -3380,7 +3394,7 @@ { "cell_type": "code", "execution_count": null, - "id": "290", + "id": "291", "metadata": {}, "outputs": [], "source": [ @@ -3400,7 +3414,7 @@ { "cell_type": "code", "execution_count": null, - "id": "291", + "id": "292", "metadata": {}, "outputs": [], "source": [ @@ -3411,7 +3425,7 @@ { "cell_type": "code", "execution_count": null, - "id": "292", + "id": "293", "metadata": {}, "outputs": [], "source": [ @@ -3420,7 +3434,7 @@ }, { "cell_type": "markdown", - "id": "293", + "id": "294", "metadata": {}, "source": [ "Once again, cached valued are just returned and not recalculated." @@ -3428,7 +3442,7 @@ }, { "cell_type": "markdown", - "id": "294", + "id": "295", "metadata": {}, "source": [ "How can we implement this as a decorator?" @@ -3437,7 +3451,7 @@ { "cell_type": "code", "execution_count": null, - "id": "295", + "id": "296", "metadata": {}, "outputs": [], "source": [ @@ -3459,7 +3473,7 @@ { "cell_type": "code", "execution_count": null, - "id": "296", + "id": "297", "metadata": {}, "outputs": [], "source": [ @@ -3472,7 +3486,7 @@ { "cell_type": "code", "execution_count": null, - "id": "297", + "id": "298", "metadata": {}, "outputs": [], "source": [ @@ -3482,7 +3496,7 @@ { "cell_type": "code", "execution_count": null, - "id": "298", + "id": "299", "metadata": {}, "outputs": [], "source": [ @@ -3492,7 +3506,7 @@ { "cell_type": "code", "execution_count": null, - "id": "299", + "id": "300", "metadata": { "lines_to_next_cell": 2 }, @@ -3503,7 +3517,7 @@ }, { "cell_type": "markdown", - "id": "300", + "id": "301", "metadata": {}, "source": [ "`fib(6)` was literally instantaneous because we already had it in the cache." @@ -3511,7 +3525,7 @@ }, { "cell_type": "markdown", - "id": "301", + "id": "302", "metadata": { "lines_to_next_cell": 2 }, @@ -3523,7 +3537,7 @@ { "cell_type": "code", "execution_count": null, - "id": "302", + "id": "303", "metadata": { "lines_to_next_cell": 2 }, @@ -3543,7 +3557,7 @@ }, { "cell_type": "markdown", - "id": "303", + "id": "304", "metadata": { "lines_to_next_cell": 2 }, @@ -3554,7 +3568,7 @@ { "cell_type": "code", "execution_count": null, - "id": "304", + "id": "305", "metadata": {}, "outputs": [], "source": [ @@ -3567,7 +3581,7 @@ { "cell_type": "code", "execution_count": null, - "id": "305", + "id": "306", "metadata": {}, "outputs": [], "source": [ @@ -3577,7 +3591,7 @@ { "cell_type": "code", "execution_count": null, - "id": "306", + "id": "307", "metadata": {}, "outputs": [], "source": [ @@ -3587,7 +3601,7 @@ { "cell_type": "code", "execution_count": null, - "id": "307", + "id": "308", "metadata": {}, "outputs": [], "source": [ @@ -3596,7 +3610,7 @@ }, { "cell_type": "markdown", - "id": "308", + "id": "309", "metadata": {}, "source": [ "Caching and decorators play a crucial role in optimizing function performance.\n", @@ -3609,7 +3623,7 @@ }, { "cell_type": "markdown", - "id": "309", + "id": "310", "metadata": {}, "source": [ "Additionally, our current implementation does not handle keyword arguments (`**kwargs`), which can be a significant limitation in more complex scenarios.\n", @@ -3623,7 +3637,7 @@ { "cell_type": "code", "execution_count": null, - "id": "310", + "id": "311", "metadata": {}, "outputs": [], "source": [ @@ -3633,7 +3647,7 @@ { "cell_type": "code", "execution_count": null, - "id": "311", + "id": "312", "metadata": {}, "outputs": [], "source": [ @@ -3646,7 +3660,7 @@ { "cell_type": "code", "execution_count": null, - "id": "312", + "id": "313", "metadata": {}, "outputs": [], "source": [ @@ -3656,7 +3670,7 @@ }, { "cell_type": "markdown", - "id": "313", + "id": "314", "metadata": {}, "source": [ "Once again, the last value `fact(8)` was simply fetched from the cache." @@ -3664,7 +3678,7 @@ }, { "cell_type": "markdown", - "id": "314", + "id": "315", "metadata": {}, "source": [ "Now let's see if we have improved on our recursive approach of calculating Fibonacci numbers.\n", @@ -3674,7 +3688,7 @@ { "cell_type": "code", "execution_count": null, - "id": "315", + "id": "316", "metadata": {}, "outputs": [], "source": [ @@ -3684,7 +3698,7 @@ { "cell_type": "code", "execution_count": null, - "id": "316", + "id": "317", "metadata": {}, "outputs": [], "source": [ @@ -3695,7 +3709,7 @@ { "cell_type": "code", "execution_count": null, - "id": "317", + "id": "318", "metadata": {}, "outputs": [], "source": [ @@ -3709,7 +3723,7 @@ { "cell_type": "code", "execution_count": null, - "id": "318", + "id": "319", "metadata": {}, "outputs": [], "source": [ @@ -3721,7 +3735,7 @@ { "cell_type": "code", "execution_count": null, - "id": "319", + "id": "320", "metadata": {}, "outputs": [], "source": [ @@ -3734,7 +3748,7 @@ }, { "cell_type": "markdown", - "id": "320", + "id": "321", "metadata": {}, "source": [ "It's about **4 orders of magnitude** faster than the naive approach! 🔥\n", @@ -3744,7 +3758,7 @@ { "cell_type": "code", "execution_count": null, - "id": "321", + "id": "322", "metadata": {}, "outputs": [], "source": [ @@ -3758,7 +3772,7 @@ { "cell_type": "code", "execution_count": null, - "id": "322", + "id": "323", "metadata": { "lines_to_next_cell": 2 }, @@ -3773,7 +3787,7 @@ }, { "cell_type": "markdown", - "id": "323", + "id": "324", "metadata": {}, "source": [ "Not the same time, but about the same order of magnitude.\n", @@ -3782,7 +3796,7 @@ }, { "cell_type": "markdown", - "id": "324", + "id": "325", "metadata": { "lines_to_next_cell": 2 }, @@ -3795,7 +3809,7 @@ { "cell_type": "code", "execution_count": null, - "id": "325", + "id": "326", "metadata": {}, "outputs": [], "source": [ @@ -3808,7 +3822,7 @@ { "cell_type": "code", "execution_count": null, - "id": "326", + "id": "327", "metadata": {}, "outputs": [], "source": [ @@ -3818,7 +3832,7 @@ { "cell_type": "code", "execution_count": null, - "id": "327", + "id": "328", "metadata": {}, "outputs": [], "source": [ @@ -3828,7 +3842,7 @@ { "cell_type": "code", "execution_count": null, - "id": "328", + "id": "329", "metadata": {}, "outputs": [], "source": [ @@ -3837,7 +3851,7 @@ }, { "cell_type": "markdown", - "id": "329", + "id": "330", "metadata": {}, "source": [ "We had to recalculate `fib(1)` because when we called `fib(9)` the least recent item in the cache (the result of `fib(1)`) was evicted from the cache." @@ -3845,7 +3859,7 @@ }, { "cell_type": "markdown", - "id": "330", + "id": "331", "metadata": {}, "source": [ "### Parametrized decorators" @@ -3853,7 +3867,7 @@ }, { "cell_type": "markdown", - "id": "331", + "id": "332", "metadata": {}, "source": [ "Here comes a natural question: what if I need to pass some argument to my decorator?\n", @@ -3862,7 +3876,7 @@ }, { "cell_type": "markdown", - "id": "332", + "id": "333", "metadata": {}, "source": [ "Let's bring back our `timed` decorator and make a small change.\n", @@ -3872,7 +3886,7 @@ { "cell_type": "code", "execution_count": null, - "id": "333", + "id": "334", "metadata": {}, "outputs": [], "source": [ @@ -3901,7 +3915,7 @@ { "cell_type": "code", "execution_count": null, - "id": "334", + "id": "335", "metadata": {}, "outputs": [], "source": [ @@ -3917,7 +3931,7 @@ { "cell_type": "code", "execution_count": null, - "id": "335", + "id": "336", "metadata": { "lines_to_next_cell": 2 }, @@ -3928,7 +3942,7 @@ }, { "cell_type": "markdown", - "id": "336", + "id": "337", "metadata": { "lines_to_next_cell": 2 }, @@ -3943,7 +3957,7 @@ { "cell_type": "code", "execution_count": null, - "id": "337", + "id": "338", "metadata": {}, "outputs": [], "source": [ @@ -3968,7 +3982,7 @@ { "cell_type": "code", "execution_count": null, - "id": "338", + "id": "339", "metadata": {}, "outputs": [], "source": [ @@ -3982,7 +3996,7 @@ { "cell_type": "code", "execution_count": null, - "id": "339", + "id": "340", "metadata": {}, "outputs": [], "source": [ @@ -3991,7 +4005,7 @@ }, { "cell_type": "markdown", - "id": "340", + "id": "341", "metadata": {}, "source": [ "But wait: why did we use the fancy `@-` syntax?\n", @@ -4000,7 +4014,7 @@ }, { "cell_type": "markdown", - "id": "341", + "id": "342", "metadata": {}, "source": [ "To fix this behavior we need to rethink of what `@` is doing.\n", @@ -4024,7 +4038,7 @@ { "cell_type": "code", "execution_count": null, - "id": "342", + "id": "343", "metadata": {}, "outputs": [], "source": [ @@ -4034,7 +4048,7 @@ { "cell_type": "code", "execution_count": null, - "id": "343", + "id": "344", "metadata": {}, "outputs": [], "source": [ @@ -4043,7 +4057,7 @@ }, { "cell_type": "markdown", - "id": "344", + "id": "345", "metadata": {}, "source": [ "So, for the syntax `@timed(10)` to work, where `10` is the number of repetition, `timed` should return **a decorator itself**, and not our closure.\n", @@ -4053,7 +4067,7 @@ { "cell_type": "code", "execution_count": null, - "id": "345", + "id": "346", "metadata": {}, "outputs": [], "source": [ @@ -4086,7 +4100,7 @@ { "cell_type": "code", "execution_count": null, - "id": "346", + "id": "347", "metadata": {}, "outputs": [], "source": [ @@ -4102,7 +4116,7 @@ { "cell_type": "code", "execution_count": null, - "id": "347", + "id": "348", "metadata": {}, "outputs": [], "source": [ @@ -4112,7 +4126,7 @@ { "cell_type": "code", "execution_count": null, - "id": "348", + "id": "349", "metadata": {}, "outputs": [], "source": [ @@ -4132,7 +4146,7 @@ { "cell_type": "code", "execution_count": null, - "id": "349", + "id": "350", "metadata": {}, "outputs": [], "source": [ @@ -4141,7 +4155,7 @@ }, { "cell_type": "markdown", - "id": "350", + "id": "351", "metadata": {}, "source": [ "And yes, you can **stack multiple decorators**! 😎" @@ -4149,7 +4163,7 @@ }, { "cell_type": "markdown", - "id": "351", + "id": "352", "metadata": {}, "source": [ "### Quiz: Decorators" @@ -4158,7 +4172,7 @@ { "cell_type": "code", "execution_count": null, - "id": "352", + "id": "353", "metadata": { "lines_to_next_cell": 2 }, @@ -4171,15 +4185,17 @@ }, { "cell_type": "markdown", - "id": "353", - "metadata": {}, + "id": "354", + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "## Generators" ] }, { "cell_type": "markdown", - "id": "354", + "id": "355", "metadata": {}, "source": [ "The concept of generators is very much tied to that of \"looping over some kind of container\".\n", @@ -4196,7 +4212,7 @@ }, { "cell_type": "markdown", - "id": "355", + "id": "356", "metadata": {}, "source": [ "To understand generators, we first need to review what it means to be **iterable** and, more importantly, what is an **iterator**.\n", @@ -4207,7 +4223,7 @@ }, { "cell_type": "markdown", - "id": "356", + "id": "357", "metadata": { "lines_to_next_cell": 2 }, @@ -4220,7 +4236,7 @@ { "cell_type": "code", "execution_count": null, - "id": "357", + "id": "358", "metadata": {}, "outputs": [], "source": [ @@ -4243,7 +4259,7 @@ { "cell_type": "code", "execution_count": null, - "id": "358", + "id": "359", "metadata": { "lines_to_next_cell": 2 }, @@ -4255,7 +4271,7 @@ }, { "cell_type": "markdown", - "id": "359", + "id": "360", "metadata": {}, "source": [ "We see that we can indeed loop over our custom `Squares` class.\n", @@ -4268,7 +4284,7 @@ }, { "cell_type": "markdown", - "id": "360", + "id": "361", "metadata": {}, "source": [ "As you might have learned by now, we can implement some built-in behavior in our classes by using the so-called \"special methods\" or **dunder methods**: `__method__`.\n", @@ -4282,7 +4298,7 @@ }, { "cell_type": "markdown", - "id": "361", + "id": "362", "metadata": {}, "source": [ "Python also has the built-in `next()` which does what you think: it takes an **iterator** object and returns the **next element** in the stream of data by calling the `__next__` method implemented by that object.\n", @@ -4295,7 +4311,7 @@ }, { "cell_type": "markdown", - "id": "362", + "id": "363", "metadata": { "lines_to_next_cell": 2 }, @@ -4306,7 +4322,7 @@ { "cell_type": "code", "execution_count": null, - "id": "363", + "id": "364", "metadata": {}, "outputs": [], "source": [ @@ -4327,7 +4343,7 @@ { "cell_type": "code", "execution_count": null, - "id": "364", + "id": "365", "metadata": { "lines_to_next_cell": 2 }, @@ -4339,7 +4355,7 @@ }, { "cell_type": "markdown", - "id": "365", + "id": "366", "metadata": {}, "source": [ "If the value returned by `square()` is 25 (our sentinel), then a `StopIteration` is raised." @@ -4347,7 +4363,7 @@ }, { "cell_type": "markdown", - "id": "366", + "id": "367", "metadata": {}, "source": [ "These two ways are identical: in the first case (the class), we built the iterator ourselves. In the second case, Python built it for us.\n", @@ -4358,7 +4374,7 @@ }, { "cell_type": "markdown", - "id": "367", + "id": "368", "metadata": { "lines_to_next_cell": 2 }, @@ -4377,7 +4393,7 @@ { "cell_type": "code", "execution_count": null, - "id": "368", + "id": "369", "metadata": {}, "outputs": [], "source": [ @@ -4393,7 +4409,7 @@ { "cell_type": "code", "execution_count": null, - "id": "369", + "id": "370", "metadata": {}, "outputs": [], "source": [ @@ -4403,7 +4419,7 @@ }, { "cell_type": "markdown", - "id": "370", + "id": "371", "metadata": {}, "source": [ "Here it is: our function returned _something_ different than the usual \"function\" object.\n", @@ -4413,7 +4429,7 @@ { "cell_type": "code", "execution_count": null, - "id": "371", + "id": "372", "metadata": {}, "outputs": [], "source": [ @@ -4423,7 +4439,7 @@ { "cell_type": "code", "execution_count": null, - "id": "372", + "id": "373", "metadata": {}, "outputs": [], "source": [ @@ -4433,7 +4449,7 @@ { "cell_type": "code", "execution_count": null, - "id": "373", + "id": "374", "metadata": {}, "outputs": [], "source": [ @@ -4443,7 +4459,7 @@ { "cell_type": "code", "execution_count": null, - "id": "374", + "id": "375", "metadata": {}, "outputs": [], "source": [ @@ -4452,7 +4468,7 @@ }, { "cell_type": "markdown", - "id": "375", + "id": "376", "metadata": {}, "source": [ "A `StopIteration` is raised if we are trying to go past the last `yield` statement.\n", @@ -4465,7 +4481,7 @@ { "cell_type": "code", "execution_count": null, - "id": "376", + "id": "377", "metadata": {}, "outputs": [], "source": [ @@ -4474,7 +4490,7 @@ }, { "cell_type": "markdown", - "id": "377", + "id": "378", "metadata": {}, "source": [ "And also the `__next__` method" @@ -4483,7 +4499,7 @@ { "cell_type": "code", "execution_count": null, - "id": "378", + "id": "379", "metadata": {}, "outputs": [], "source": [ @@ -4492,7 +4508,7 @@ }, { "cell_type": "markdown", - "id": "379", + "id": "380", "metadata": {}, "source": [ "We can also check that `iter()` applied on our object returns indeed the same thing.\n", @@ -4502,7 +4518,7 @@ { "cell_type": "code", "execution_count": null, - "id": "380", + "id": "381", "metadata": {}, "outputs": [], "source": [ @@ -4512,7 +4528,7 @@ { "cell_type": "code", "execution_count": null, - "id": "381", + "id": "382", "metadata": {}, "outputs": [], "source": [ @@ -4521,7 +4537,7 @@ }, { "cell_type": "markdown", - "id": "382", + "id": "383", "metadata": {}, "source": [ "Precisely the same object." @@ -4529,7 +4545,7 @@ }, { "cell_type": "markdown", - "id": "383", + "id": "384", "metadata": {}, "source": [ "How does Python know when to stop the iteration?\n", @@ -4546,7 +4562,7 @@ }, { "cell_type": "markdown", - "id": "384", + "id": "385", "metadata": {}, "source": [ "Here's a generator that yields numbers and returns a message at the end:" @@ -4555,7 +4571,7 @@ { "cell_type": "code", "execution_count": null, - "id": "385", + "id": "386", "metadata": { "lines_to_next_cell": 1 }, @@ -4570,7 +4586,7 @@ }, { "cell_type": "markdown", - "id": "386", + "id": "387", "metadata": {}, "source": [ "When we iterate through the generator in a for loop, we only see the yielded values:" @@ -4579,7 +4595,7 @@ { "cell_type": "code", "execution_count": null, - "id": "387", + "id": "388", "metadata": {}, "outputs": [], "source": [ @@ -4589,7 +4605,7 @@ }, { "cell_type": "markdown", - "id": "388", + "id": "389", "metadata": {}, "source": [ "But we can capture the return value by catching the StopIteration exception:" @@ -4598,7 +4614,7 @@ { "cell_type": "code", "execution_count": null, - "id": "389", + "id": "390", "metadata": { "lines_to_next_cell": 1 }, @@ -4615,7 +4631,7 @@ }, { "cell_type": "markdown", - "id": "390", + "id": "391", "metadata": {}, "source": [ "This ability to attach return values to generators is used internally by Python features like `yield from`.\n", @@ -4626,7 +4642,7 @@ { "cell_type": "code", "execution_count": null, - "id": "391", + "id": "392", "metadata": { "lines_to_next_cell": 1 }, @@ -4642,7 +4658,7 @@ { "cell_type": "code", "execution_count": null, - "id": "392", + "id": "393", "metadata": { "lines_to_next_cell": 1 }, @@ -4658,7 +4674,7 @@ { "cell_type": "code", "execution_count": null, - "id": "393", + "id": "394", "metadata": {}, "outputs": [], "source": [ @@ -4669,7 +4685,7 @@ }, { "cell_type": "markdown", - "id": "394", + "id": "395", "metadata": { "lines_to_next_cell": 2 }, @@ -4683,7 +4699,7 @@ { "cell_type": "code", "execution_count": null, - "id": "395", + "id": "396", "metadata": {}, "outputs": [], "source": [ @@ -4700,7 +4716,7 @@ { "cell_type": "code", "execution_count": null, - "id": "396", + "id": "397", "metadata": {}, "outputs": [], "source": [ @@ -4711,7 +4727,7 @@ { "cell_type": "code", "execution_count": null, - "id": "397", + "id": "398", "metadata": {}, "outputs": [], "source": [ @@ -4721,7 +4737,7 @@ { "cell_type": "code", "execution_count": null, - "id": "398", + "id": "399", "metadata": {}, "outputs": [], "source": [ @@ -4731,7 +4747,7 @@ { "cell_type": "code", "execution_count": null, - "id": "399", + "id": "400", "metadata": { "lines_to_next_cell": 2 }, @@ -4742,7 +4758,7 @@ }, { "cell_type": "markdown", - "id": "400", + "id": "401", "metadata": {}, "source": [ "Note how in the generator function above we incremented the number `i` **after** the `yield` statement.\n", @@ -4751,7 +4767,7 @@ }, { "cell_type": "markdown", - "id": "401", + "id": "402", "metadata": {}, "source": [ "### Create an interable from a generator" @@ -4759,7 +4775,7 @@ }, { "cell_type": "markdown", - "id": "402", + "id": "403", "metadata": {}, "source": [ "As we know, generators are iterators.\n", @@ -4771,7 +4787,7 @@ }, { "cell_type": "markdown", - "id": "403", + "id": "404", "metadata": { "lines_to_next_cell": 2 }, @@ -4782,7 +4798,7 @@ { "cell_type": "code", "execution_count": null, - "id": "404", + "id": "405", "metadata": {}, "outputs": [], "source": [ @@ -4794,7 +4810,7 @@ { "cell_type": "code", "execution_count": null, - "id": "405", + "id": "406", "metadata": {}, "outputs": [], "source": [ @@ -4804,7 +4820,7 @@ { "cell_type": "code", "execution_count": null, - "id": "406", + "id": "407", "metadata": {}, "outputs": [], "source": [ @@ -4814,7 +4830,7 @@ }, { "cell_type": "markdown", - "id": "407", + "id": "408", "metadata": {}, "source": [ "But our generator is now exhausted and it has nothing left to return:" @@ -4823,7 +4839,7 @@ { "cell_type": "code", "execution_count": null, - "id": "408", + "id": "409", "metadata": { "lines_to_next_cell": 2 }, @@ -4834,7 +4850,7 @@ }, { "cell_type": "markdown", - "id": "409", + "id": "410", "metadata": { "lines_to_next_cell": 2 }, @@ -4846,7 +4862,7 @@ { "cell_type": "code", "execution_count": null, - "id": "410", + "id": "411", "metadata": {}, "outputs": [], "source": [ @@ -4861,7 +4877,7 @@ { "cell_type": "code", "execution_count": null, - "id": "411", + "id": "412", "metadata": {}, "outputs": [], "source": [ @@ -4871,7 +4887,7 @@ }, { "cell_type": "markdown", - "id": "412", + "id": "413", "metadata": {}, "source": [ "And we can do it again:" @@ -4880,7 +4896,7 @@ { "cell_type": "code", "execution_count": null, - "id": "413", + "id": "414", "metadata": { "lines_to_next_cell": 2 }, @@ -4891,7 +4907,7 @@ }, { "cell_type": "markdown", - "id": "414", + "id": "415", "metadata": { "lines_to_next_cell": 2 }, @@ -4902,7 +4918,7 @@ { "cell_type": "code", "execution_count": null, - "id": "415", + "id": "416", "metadata": {}, "outputs": [], "source": [ @@ -4922,7 +4938,7 @@ { "cell_type": "code", "execution_count": null, - "id": "416", + "id": "417", "metadata": {}, "outputs": [], "source": [ @@ -4932,7 +4948,7 @@ { "cell_type": "code", "execution_count": null, - "id": "417", + "id": "418", "metadata": {}, "outputs": [], "source": [ @@ -4942,7 +4958,7 @@ { "cell_type": "code", "execution_count": null, - "id": "418", + "id": "419", "metadata": { "lines_to_next_cell": 2 }, @@ -4953,7 +4969,7 @@ }, { "cell_type": "markdown", - "id": "419", + "id": "420", "metadata": {}, "source": [ "### Combining generators" @@ -4961,7 +4977,7 @@ }, { "cell_type": "markdown", - "id": "420", + "id": "421", "metadata": { "lines_to_next_cell": 2 }, @@ -4973,7 +4989,7 @@ { "cell_type": "code", "execution_count": null, - "id": "421", + "id": "422", "metadata": {}, "outputs": [], "source": [ @@ -4985,7 +5001,7 @@ { "cell_type": "code", "execution_count": null, - "id": "422", + "id": "423", "metadata": {}, "outputs": [], "source": [ @@ -4995,7 +5011,7 @@ { "cell_type": "code", "execution_count": null, - "id": "423", + "id": "424", "metadata": {}, "outputs": [], "source": [ @@ -5004,7 +5020,7 @@ }, { "cell_type": "markdown", - "id": "424", + "id": "425", "metadata": {}, "source": [ "Now, `enumerate` builds a generator itself, so `sq` had not been consumed yet at this point:" @@ -5013,7 +5029,7 @@ { "cell_type": "code", "execution_count": null, - "id": "425", + "id": "426", "metadata": {}, "outputs": [], "source": [ @@ -5023,7 +5039,7 @@ { "cell_type": "code", "execution_count": null, - "id": "426", + "id": "427", "metadata": {}, "outputs": [], "source": [ @@ -5032,7 +5048,7 @@ }, { "cell_type": "markdown", - "id": "427", + "id": "428", "metadata": {}, "source": [ "But since we now have consumed **2 elements** from `sq`, when we use `enumerate` it will also have two less items from `sq`:" @@ -5041,7 +5057,7 @@ { "cell_type": "code", "execution_count": null, - "id": "428", + "id": "429", "metadata": { "lines_to_next_cell": 1 }, @@ -5052,7 +5068,7 @@ }, { "cell_type": "markdown", - "id": "429", + "id": "430", "metadata": {}, "source": [ "And this might not be what you expected: the value is the **third** element of `sq` ($2^2$), while the index is `0`, as if we were starting from the beginning.\n", @@ -5063,7 +5079,7 @@ }, { "cell_type": "markdown", - "id": "430", + "id": "431", "metadata": {}, "source": [ "### Beware: Generators can only be consumed once!\n", @@ -5074,7 +5090,7 @@ { "cell_type": "code", "execution_count": null, - "id": "431", + "id": "432", "metadata": { "lines_to_next_cell": 1 }, @@ -5088,7 +5104,7 @@ { "cell_type": "code", "execution_count": null, - "id": "432", + "id": "433", "metadata": {}, "outputs": [], "source": [ @@ -5100,7 +5116,7 @@ { "cell_type": "code", "execution_count": null, - "id": "433", + "id": "434", "metadata": {}, "outputs": [], "source": [ @@ -5113,7 +5129,7 @@ { "cell_type": "code", "execution_count": null, - "id": "434", + "id": "435", "metadata": {}, "outputs": [], "source": [ @@ -5125,7 +5141,7 @@ }, { "cell_type": "markdown", - "id": "435", + "id": "436", "metadata": {}, "source": [ "Compare this with a list, which can be iterated multiple times:" @@ -5134,7 +5150,7 @@ { "cell_type": "code", "execution_count": null, - "id": "436", + "id": "437", "metadata": {}, "outputs": [], "source": [ @@ -5146,7 +5162,7 @@ { "cell_type": "code", "execution_count": null, - "id": "437", + "id": "438", "metadata": {}, "outputs": [], "source": [ @@ -5159,7 +5175,7 @@ { "cell_type": "code", "execution_count": null, - "id": "438", + "id": "439", "metadata": {}, "outputs": [], "source": [ @@ -5171,7 +5187,7 @@ }, { "cell_type": "markdown", - "id": "439", + "id": "440", "metadata": {}, "source": [ "If you need to iterate through generator values multiple times, convert it to a list first:\n", @@ -5184,7 +5200,7 @@ }, { "cell_type": "markdown", - "id": "440", + "id": "441", "metadata": {}, "source": [ "### Quiz: Generators" @@ -5193,7 +5209,7 @@ { "cell_type": "code", "execution_count": null, - "id": "441", + "id": "442", "metadata": {}, "outputs": [], "source": [ @@ -5204,7 +5220,7 @@ }, { "cell_type": "markdown", - "id": "442", + "id": "443", "metadata": {}, "source": [ "## Exercises" @@ -5213,7 +5229,7 @@ { "cell_type": "code", "execution_count": null, - "id": "443", + "id": "444", "metadata": {}, "outputs": [], "source": [ @@ -5222,7 +5238,7 @@ }, { "cell_type": "markdown", - "id": "444", + "id": "445", "metadata": {}, "source": [ "### Exercise 1: Password checker factory" @@ -5230,7 +5246,7 @@ }, { "cell_type": "markdown", - "id": "445", + "id": "446", "metadata": { "jp-MarkdownHeadingCollapsed": true }, @@ -5254,7 +5270,7 @@ }, { "cell_type": "markdown", - "id": "446", + "id": "447", "metadata": {}, "source": [ "For example, to create a password checker that requires a password to have at least 2 uppercase letters, at least 3 lowercase letters, at least 1 punctuation mark, and at least 4 digits, we can write\n", @@ -5284,7 +5300,7 @@ { "cell_type": "code", "execution_count": null, - "id": "447", + "id": "448", "metadata": {}, "outputs": [], "source": [ @@ -5310,20 +5326,24 @@ " min_dig: Minimum number of digits\n", " Returns:\n", " - A function that checks if a password meets the criteria\n", - " \"\"\"" + " \"\"\"\n", + " def noop(*args, **kwargs): ...\n", + " return noop" ] }, { "cell_type": "markdown", - "id": "448", - "metadata": {}, + "id": "449", + "metadata": { + "jp-MarkdownHeadingCollapsed": true + }, "source": [ "### Exercise 2: String range" ] }, { "cell_type": "markdown", - "id": "449", + "id": "450", "metadata": {}, "source": [ "Complete the function called `solution_str_range` so that it emulates the built-in `range`, but for characters.\n", @@ -5337,7 +5357,7 @@ }, { "cell_type": "markdown", - "id": "450", + "id": "451", "metadata": {}, "source": [ "