diff --git a/Academy DSA Typed Notes/Python Refresher/Refresher Functions b/Academy DSA Typed Notes/Python Refresher/Refresher Functions new file mode 100644 index 0000000..71e57fb --- /dev/null +++ b/Academy DSA Typed Notes/Python Refresher/Refresher Functions @@ -0,0 +1,601 @@ +# Refresher: Functions + +### Recap + +* DRY RUN - Running code line by line, we run the code just like the compiler does without using human intelligence. +* Loops - instead of writing the code multiple times, we use loops to perform the same task +* Nested Loops - These are loops inside loops. +* We usually use `i` for rows and `j` for columns. +--- + +### QUIZ 1 + +What will the sequence be generated by the following? + +```c +range(-5,0,1) +``` + + +### Choices +- [ ] -5,-4,-3,-2,-1,0 +- [ ] Nothing +- [ ] 0,-1,-2,-3,-4,-5 +- [x] -5,-4,-3,-2,-1 +--- + + +### QUIZ 2 + +What will the sequence be generated by the following? + +```c +range(-10,-5,-1) +``` + +### Choices +- [ ] -10,-9,-8,-7,-6,-5 +- [ ] -10,-9,-8,-7,-6 +- [x] Nothing +- [ ] -6,-7,-8,-9,-10 +--- + +### QUIZ 3 + +What will be the output of the following? + +```cpp +for i in range(1,10): + if(i % 3 == 0): + break + print(i, end = ' ') +``` + +### Choices + +- [ ] 1 2 3 4 5 6 7 8 9 +- [x] 1 2 +- [ ] 1 2 4 5 7 8 9 +- [ ] 1 2 4 5 6 7 8 +--- + + +### QUIZ 4 + +What will be the output of the following? + +```cpp +for i in range(0,4): + for j in range(0,i): + print('a', end = '') + print() +``` + +### Choices +- [ ] a +aa +aaa + +- [x] aaaa +aaaa +aaaa +aaaa + +- [ ] aaa +aa +a +--- + +## Need for functions + + +* We are working at a banking facility. We are to code the receipt that is printed after the ATM transaction. It is something like: + +```cpp +****************** +Transaction Complete +Thank you for visiting +****************** +``` +so we will write the code for this: + +```python +print('*' * 5) +print("Transaction complete") +print("Thank you for visiting") +print('*' * 5) +``` +* Now for every type of transaction same message will be printed. And for every transaction contained in the big file of 10000 lines of code, we have the same 4 lines for different transactions. +* This is not following the DRY(Do not repeat yourself) principle. Which is a bad practice. +* Let us say the back says they don't want to print `thank you` they wish to print `thanks` instead. So we will have to update it at all the places separately. +* Functions are like alias - I can write a line of code and I can name it `abc`. Everywhere I can call abc the same piece of code will be executed. +* Functions make code modular. +* Now if there are any changes, we change it in one place and it will reflect in all the other places. + +## Syntax of Defining a Function +**Defining a function** +```python +def function_name(): + # actions +``` + +**To Call the function** +```python +function_name() +``` + + + + +* The naming convention for a function is the same as variables. + * It should be composed of alphabet, number, and underscore + * should start with an alphabet or underscore + * It should not be a keyword +* In Python we use snake case. (there are two cases camel case and snake case). + +## Return Statement +* Functions not only put some code in a block they return something. +* Example function +```python +def area_circle_rad_5(){ + print(3.14 * 5 * 5) +} +area_circle_rad_5() +``` +**Output** +```plaintext +78.5 +``` +* This is how we define a function and call it. Now we want to add `1` to it or `2` to it. Can't we take it to a variable? +* This is what return statements are for. It is like a black box where you want some task to be done. But after the task is done you want it to return some value as well. +* So the syntax is something like:` +```python +def function_name(): + return 5; + +abc() +a = abc() +print(a) +``` +**Output** +```plaintext +5 +``` +* We are storing the return value of the function in a variable `a`. Now we can print this variable as usual. +* Unlike other programming languages in Python we are not required to define the return type with the function name. + + +**Note:** Functions execution ends at return. Once a return statement is found rest of the lines of the code will not be executed. It is just like a break in loops. +If you do return without any value, it will return `None`. +```python +def abc(): + return +abc() +``` + +## Parameter and Argument in Function +* Let us say we want to calculate the area of a circle whose radius `10`. We will again write the code as +```python +def area_circle_rad_10(): + print(3.14 * 10 * 10) +``` +* So for every different radius we will write a different code? This is not a good practice. +* In Python we can take as many parameters as required. So we can simply take radius as a parameter from the use. +```python +def area_circle(radius): + return (3.14 * (radius ** 2)) + +area = area_circle(5) +``` +* If you want multiple parameters you can simply separate them by comma, their types are not required. +* If the function is not returning anything by default it will return `None`. +* In Python, a parameter is a variable used in a function definition, while an argument is an actual value passed to the function during a call. +* About the above function `radius` used in the `area_circle()` function is a parameter, the `5` passed to it while calling is an argument. +* But people use it interchangeably. + +## Execution of A Function in Python +* Python compiler executes line-by-line +* Everything in Python is an object of a class. +* Function is also a class. +* Function is just like a variable, a variable gets declared in a single line, and functions get declared in multiple lines. +* In Python if you call a function without defining it, it will show an error. +* We can call a function inside a function. +* Let us say we have a bank function to do transactions for saving accounts. We have multiple functions to check the balance, to execute the transaction, to print the thank you message, etc. + +## Scope of a Variable +```python +a = 1 +def arae_circle(radius): + area = 3.14 * (radius ** 2) + return area +b = area_circle(10) +print(a) +print(area) +print(b) +``` +* Whatever we have done so far, we have done it in global scope. +* Here `a` and `b` are global variables. But `area` is a local variable to the function `area_circle`. It can only be accessed in the function. As the execution of the function is over the Python compiler frees up the memory. Thus `area` is no longer a valid memory location. +* Though we can do this: +```python +a = 1 +def arae_circle(radius): + area = 3.14 * (radius ** 2) + print(a) + return area +b = area_circle(10) +print(a) +print(area) # this will give error +print(b) +``` +* This is because `a` is a global variable and can be accessed everywhere. + + +* In Python variables defined in `if` or `for` loops can be used outside their block as well but it is not true for the functions. + +```python +a = 1 +def abd(): + a = 2 + print(a) +abc() +``` + +**Output** +```plaintext +2 +1 +``` +* So, this will have two variables called `a`. One is global and another is local. When we say `print(a)` it will refer to the local variable and print its value. +* To update or use global we use the following syntax: + +```python +a = 1 +def abc(): + global a + a = 2 + print(a) +abc() +print(a) +``` +**Output** +```plaintext +2 +2 +``` +Here global variable `a` is reused and its value is changed inside a function. + +--- + +### QUIZ 5 + +Is this a valid function? + +```python +def print_hello: + print('Hello') +``` +### Choices +- [ ] Yes +- [x] No + +There are no paranthesis in the function and they are must in a function. + +```python +def print_hello(): + print('Hello') +``` +--- + +### QUIZ 6 + +Is this a valid function? + +```python +def print_name(): + print('Hello', name) +``` + + +# Choices +- [ ] Yes +- [x] No + +The variable name must be taken as an argument. + +--- + +### QUIZ 7 + +How many times the function foo is called? + +```python + +foo() +foo() +for i in range(1,5): + foo() +``` + +### Choices +- [ ] 5 +- [x] 6 +- [ ] 7 +- [ ] 8 +--- + +### QUIZ 8 + +What will be the output of the following? + +```python +def area_rectangle(l, b): + area = l * b +area_circle(3, 4) +print(area) +``` +# Choices +- [ ] 12 +- [ ] 7 +- [x] Error +--- + +There is no function named `area_cirle()`. + +### QUIZ 9 + +What will be the output of the following? + +```python +def foo(a): + if(a): + return 100 + a += 20 + return a +print(foo(0)) +``` +### Choices +- [ ] 100 +- [ ] 120 +- [x] 20 +- [ ] Nothing + +A function's execution ends once a `return` is encountered. + +If you do return without any value, it will return None. Though this is not helpful it is still valid. +```python +def abc(): + return +abc() +``` + +### QUIZ 10 + +What will be the output of the following? + +```python +def solve(a, b): + print('Function Started') + return 0 + c = a + b + print('Function Ends') +c = solve(2, 3) +print(c) +``` +### Choices +- [ ] Function Started +Function Ends +5 +- [ ] Function Started +Function Ends +0 + +- [ ] Function Started +0 +Function Ends + +- [x] Function Started +0 + + +We are doing early exit via return `0` and therefore the result is `0`. + + +## Positional and Keyword Arguments +Let us consider an example: +```python +def bio_data(name, age, gender): + print("Name: ", name) + print("Age: ", age) + print("Gender: ", gender) + +bio_data(23, "Male", "Aakar") +``` + +#### **`What is the problem with the above code?`** + +* **The order of the parameter matters**, age will printed with the name, and so on +* Python has two types of arguments **Positonal** and **Keyword**. +* The above function call is referred to as positional arguments. Where order or position of arguments matters. +* The other one is keyword argument. Here we have to give the name of the argument but the order doesn't matter. +* The name must be the same as given in the function definition. +```python +bio_data(name = "Aakar", gender = "Male", age = 23) +``` +**Note:** A single call can have both together, but the Keyword argument must always be after positional arguments. Python will show an error if not done this way. +**Cannot Do** +```python +bio_data("Aakar", age = 23, "Male") +``` +**Must Do** +```python +bio_data("Aakar", age = 23,gender = "Male") +``` + +## Default and Non-default Arguments +* Let us say we are writing the above function. For example, people don't like having to declare their gender. So, we can give the default argument gender with the value `Unspecified` in Python. +```python +def bio_data(name, age, gender = "Unspecified"): + print("Name: ", name) + print("Age: ", age) + print("Gender: ", gender) + +bio_data(23, "Aakar") +``` +Now, it is both ways okay even if we define or do not define the argument. + +**Note:** A single call can have both together, but the Non-default argument must always be after default arguments. Python will show an error if not done this way. +**Cannot Do** +```python +bio_data(name, age = 23, gender) +``` +**Must Do** +```python +def bio_data(name, age, gender = "Unspecified"): +``` + +### QUIZ 11 + +What is the correct way of calling this function? + +```python +def print_biodata(name, age, gender): + print('Name', name) + print('Age', age) + print('Gender', gender) +``` + +### Choices +- [ ] print_biodata('Hari', 'Male', 23) + +- [ ] print_biodata('Male', 'Hari', 23) + +- [x] print_biodata('Hari', 23, 'Male') + +- [ ] print_biodata(23, 'Hari', 'Male') +--- + + +### QUIZ 12 + +What will be the output of the following? + +```python +def print_biodata(name, age = 23, gender = 'Female'): + print('Name', name) + print('Age', age) + print('Gender', gender) +``` +### Choices +- [x] Yes +- [ ] No + +First there should be non-default arguments and then the default arguments. + +### QUIZ 13 + +Which of the function call is correct? + +```python +def print_biodata(name, age = 23, gender = 'Female'): + print('Name', name) + print('Age', age) + print('Gender', gender) +``` + +### Choices +- [ ] `print_biodata(name='Hari', 23)` +- [ ] `print_biodata('Hari', 23, age=23)` +- [x] `print_biodata('Hari')` +- [ ] `print_biodata()` + +* 1st option - `name` is a keyword argument and the age is a positional argument. Positional argument cannot come after keyword argument. +* 2nd option - In the above quiz we are defining age twice which is not valid. +* 3rd argument - Rest two arguments are default +* 4th option - We still need name argument as name is not a default argument. + +* `*args` - opens a list/tuple (arguments) +* **`kwags` - opens a dictionary (keyword arguments) +* `*` is used for opening a single-dimension iterator. +* Let us say there exists a function that takes an unspecified number of parameters. +* This can be achieved with the help of `*args` and `**kwargs`. This lets us pass as many arguments as we want. + +```python +def abc(a, b, *args, **kwargs): + print(a, b) + print(args) + print(kwargs) + +abc(2,3,4,5, name = "Hari",age = 23) +``` +**Output** +```plaintext +2 3 +[4, 5] +{"name" : "Hari" +"age" : 23} +``` +* You cannot have more than one `*args` or `**kwargs` as their behavior of it is unspecified. + + +## Docstrings +* Python provides a special functionality **doc strings** just like comments but slightly different than multiline comments. +* If someone wants to know how the function works they have to come to the code and see. So many Python code readers and default Python provides a documentation of functions and classes and these are called **Doc String** +* You have to write a multiline comment right after the function definition. +```python +def bio_data(name, age, gender = "Unspecified"): + """ + This function prints the bio-data using the information provided + + + Args: + name + age + gender + + Returns: + None + """ + print("Name: ", name) + print("Age: ", age) + print("Gender: ", gender) +``` +To read the doc string, just add a `?` after it. +```python +bio_data? +``` + +## Question - Simple Interest +* Take two parameters, how much they want to invest, and how long they want to invest it for. +* Simple interest - 7%. +* Write a function `print_interest` + + +```python +si = 7 +principal = int(input("How much you want to invest>")) +time = float(input("How long you want to invest it for (in years)?")) + +def print_interest(principle, time): + result = principle * time * si / 100 + return result + +result = print_interest(principle, time) +print(result) +``` + +### Question - Simple Interest +* **Round function** in Python rounds the number. It takes the number and the precision we want. It rounds off this number and returns it. +```python +round(3.1415,2) -> 3.14 +``` +* Given a radius `R`, print the area of a circle but up to two decimal places only. + + +```python +PI = 3.14159 +def print_area(radius): + area = PI * (radius ** 2) + +result = print_area(5) +print(result) +```