alexmojaki / futurecoder

100% free and interactive Python course for beginners
https://futurecoder.io/
MIT License
1.31k stars 140 forks source link

Functions chapter #93

Open alexmojaki opened 4 years ago

alexmojaki commented 4 years ago

Some things to add to the current functions chapter:

Things that will only be covered in a later chapter:

spamegg1 commented 3 years ago

I think this goes here:

Page on `return` to stop execution early # Early Return ## Page: More on the `return` statement ### Step: double return in one function (VerbatimStep) Sometimes `return` can be a source of confusion and mistakes for new learners. Let's learn more about how `return` works, how it interacts with `if` statements and `for` loops. First let's take a look at what happens when a function contains multiple `return` statements. What do you think the following code will do? ```python def foo(): return 1 return 2 print(foo()) ``` *Predicted output choices:* - `1` - `2` - `1` `2` - `1 2` - `Error` *user runs code, predicts output, reaches next Step* ### Step: cannot return multiple values (VerbatimStep) In the case of multiple `return` statements it's first reached, first served. Whichever `return` is reached first by the code is the one that gets executed. Once a `return` statement is executed, the function will stop, and the rest of the code will not get executed. This means that it's possible to write unreachable `return` statements like the above: `return 2` can *never* be reached no matter how many times we run this function! Because `return 1` will always be executed first and stop the function. ***One, and only one `return` can be executed per function call (which will stop the execution)!*** So multiple `return` statements can be useful only in the case of multiple branches in the code, such as an `if-else` block. A common mistake is to misuse `if` conditions to create unreachable `return` statements: ```python if condition: return 1 else: return 2 ``` If `condition` always ends up being `True` then the `else` part will never be executed, so `return 2` can never be reached! Similarly if `condition` always ends up being `False` then `return 1` will never be reached. This means that you have to think carefully about your conditions and all the possibilities. Moreover since the first `return 1` statement would stop the execution if it's reached, the `else` is not necessary. The above is equivalent to: ```python if condition: return 1 return 2 ``` Another common mistake is to misunderstand what `return` does in `for` loops. Try the following: ```python def double_numbers(numbers): for x in numbers: return x * 2 assert_equal(double_numbers([1, 2, 3]), [2, 4, 6]) ``` *user runs code, reaches next Step* ### Step: correct way to return: building a list and appending (ExerciseStep) At first it may look intuitive to combine `return` with lists and `for` loops as above: **"return one thing for each thing in a list"**. But it doesn't work like that! If you inspect the code with *Snoop* you'll see what is happening clearly: - the call to `double_numbers` begins, and then - only the first step of the `for` loop is executed (which sets up `x = 1`), - after which `1 * 2 = 2` is returned and the function stops! - Then `assert_equal` rightfully complains that `2 != [2, 4, 6]`. So we cannot use `return` to output multiple values like that. Now I'd like you to fix the code, so that the `assert_equal` statement gives `OK`. It should also work correctly for any list of `numbers`. *Hints:* *What do you need to return to make the assertion correct?* *What needs to be returned can be built up step by step with the for loop.* *What method/function can you use to build up the result you need?* *Solution:* ```python def double_numbers(numbers): doubles = [] for x in numbers: doubles.append(x * 2) return doubles ``` *user solves exercise, reaches next Step* ### Step: `return` ends the whole function, not just the loop (VerbatimStep) Excellent! Now you know how to correctly return multiple values by building up lists. Above we saw that `return` stopped a `for` loop only after the first step of the loop. What happens if there are nested loops? Try the following function: ```python def foo(): for letter in 'abcde': for number in range(3): print(f"{letter} {number}") if letter == 'c': return letter foo() ``` *Predicted output choices:* - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 ``` - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 c 1 c 2 ``` - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 d 0 d 1 d 2 e 0 e 1 e 2 ``` - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 c 1 c 2 d 0 d 1 d 2 e 0 e 1 e 2 ``` - `Error` *user runs code, predicts output, reaches next Step* ### Step: `break` vs `return`: `break` only stops the inner loop (VerbatimStep) As you see `return` does not only stop the inner loop or the outer loop, but ***it stops the whole function.*** Previously you learned a way to stop loops early, by using `break`. Let's compare `break` to `return` and see how they are different. Change the function as follows: ```python def foo(): for letter in 'abcde': for number in range(3): print(f"{letter} {number}") if letter == 'c': break return letter print(foo()) ``` *Predicted output choices:* - ```python a 0 a 1 a 2 b 0 b 1 b 2 c ``` - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 c ``` - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 c 1 c 2 c ``` - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 d 0 d 1 d 2 e 0 e 1 e 2 e ``` - ```python a 0 a 1 a 2 b 0 b 1 b 2 c 0 c 1 c 2 d 0 d 1 d 2 e 0 e 1 e 2 e ``` - `Error` *user runs code, predicts output, reaches Final Text* ### Final Text: using `return` to your advantage Unlike `return`, `break` only stops the loop in which it is used. In this case only the inner loop `for number in range(3):` is stopped by `break`: - For `letter = c`, the line `print(f"{letter} {number}")` is executed only for `number = 0`, - then the inner loop is stopped by `break`, but - the outer loop continues its execution, moving on to the next letter `d`, and then `e`, - the final value of `letter` is `e` so that is returned by the function. *user reaches next Page*
alexmojaki commented 3 years ago

Leave out the if/else part. I often include the else as an intentional stylistic choice. "If condition always ends up being True" then there's a bigger problem which has little to do with this chapter.

There's already a double_numbers step in chapter 6. They should already know how to do this, we're just quickly reminding them that this is the correct way. I think making them do this as an exercise is too tedious.

This leaves the page with no exercises, which is maybe a problem, but i think it's ok.

All the code should be copyable, except the last one. The user should only have to change return letter to break so that they understand the difference. No need to print the return value.