1. A function is a block of code that performs a specific task when the function is called (invoked). You can use functions to make your code reusable, better organized, and more readable. Functions can have parameters and return values.
2. There are at least four basic types of functions in Python:
- built-in functions which are an integral part of Python (such as the
print()
function). You can see a complete list of Python built-in functions at https://docs.python.org/3/library/functions.html. - the ones that come from pre-installed modules.
- user-defined functions which are written by users for users – you can write your own functions and use them freely in your code,
- the
lambda
functions
3. You can define your own function using the def
keyword and the following syntax:
1 2 |
def your_function(optional parameters): # the body of the function |
You can define a function which doesn’t take any arguments, e.g.:
1 2 3 4 |
def message(): # defining a function print("Hello") # body of the function message() # calling the function |
You can define a function which takes arguments, too, just like the one-parameter function below:
1 2 3 4 5 6 |
def hello(name): # defining a function print("Hello,", name) # body of the function name = input("Enter your name: ") hello(name) # calling the function |
4. You can pass information to functions by using parameters. Your functions can have as many parameters as you need.
An example of a one-parameter function:
1 2 3 4 |
def hi(name): print("Hi,", name) hi("Greg") |
An example of a two-parameter function:
1 2 3 4 5 |
def hi_all(name_1, name_2): print("Hi,", name_2) print("Hi,", name_1) hi_all("Sebastian", "Konrad") |
An example of a three-parameter function:
1 2 3 4 5 6 7 8 |
def address(street, city, postal_code): print("Your address is:", street, "St.,", city, postal_code) s = input("Street: ") p_c = input("Postal Code: ") c = input("City: ") address(s, c, p_c) |
5. You can pass arguments to a function using the following techniques:
- positional argument passing in which the order of arguments passed matters (Ex. 1),
- keyword (named) argument passing in which the order of arguments passed doesn’t matter (Ex. 2),
- a mix of positional and keyword argument passing (Ex. 3).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Ex. 1 def subtra(a, b): print(a - b) subtra(5, 2) # outputs: 3 subtra(2, 5) # outputs: -3 Ex. 2 def subtra(a, b): print(a - b) subtra(a=5, b=2) # outputs: 3 subtra(b=2, a=5) # outputs: 3 Ex. 3 def subtra(a, b): print(a - b) subtra(5, b=2) # outputs: 3 subtra(5, 2) # outputs: 3 |
It’s important to remember that you have to put positional arguments before keyword arguments. Positional arguments mustn’t follow keyword arguments. That’s why if you try to run the following snippet:
1 2 3 4 5 |
def subtra(a, b): print(a - b) subtra(5, b=2) # outputs: 3 subtra(a=5, 2) # Syntax Error |
Python will not let you do it by signalling a SyntaxError.
6. You can use the keyword argument passing technique to pre-define a value for a given argument:
1 2 3 4 5 |
def name(first_name, last_name="Smith"): print(first_name, last_name) name("Andy") # outputs: Andy Smith name("Betty", "Johnson") # outputs: Betty Johnson (the keyword argument replaced by "Johnson") |
7. You can use the return
keyword to tell a function to return some value. The return statement exits the function, e.g.:
1 2 3 4 5 6 7 |
def multiply(a, b): return a * b print(multiply(3, 4)) # outputs: 12 def multiply(a, b): return print(multiply(3, 4)) # outputs: None |
8. The result of a function can be easily assigned to a variable, e.g.:
1 2 3 4 5 6 |
def wishes(): return "Happy Birthday!" w = wishes() print(w) # outputs: Happy Birthday! |
Look at the difference in output in the following two examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Example 1 def wishes(): print("My Wishes") return "Happy Birthday" wishes() # outputs: My Wishes # Example 2 def wishes(): print("My Wishes") return "Happy Birthday" print(wishes()) # outputs: My Wishes # Happy Birthday |
9. You can use a list as a function’s argument, e.g.:
1 2 3 4 5 |
def hi_everybody(my_list): for name in my_list: print("Hi,", name) hi_everybody(["Adam", "John", "Lucy"]) |
10. A list can be a function result, too, e.g.:
1 2 3 4 5 6 7 |
def create_list(n): my_list = [] for i in range(n): my_list.append(i) return my_list print(create_list(5)) |
11. You can use the return
keyword to tell a function to return some value. The return
statement exits the function, e.g.:
1 2 3 4 5 6 7 8 9 |
def multiply(a, b): return a * b print(multiply(3, 4)) # outputs: 12 def multiply(a, b): return print(multiply(3, 4)) # outputs: None |
12. The result of a function can be easily assigned to a variable, e.g.:
1 2 3 4 5 |
def wishes(): return "Happy Birthday!" w = wishes() print(w) # outputs: Happy Birthday! |
Look at the difference in output in the following two examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Example 1 def wishes(): print("My Wishes") return "Happy Birthday" wishes() # outputs: My Wishes # Example 2 def wishes(): print("My Wishes") return "Happy Birthday" print(wishes()) # outputs: My Wishes # Happy Birthday |
13. You can use a list as a function’s argument, e.g.:
1 2 3 4 5 |
def hi_everybody(my_list): for name in my_list: print("Hi,", name) hi_everybody(["Adam", "John", "Lucy"]) |
14. A list can be a function result, too, e.g.:
1 2 3 4 5 6 7 |
def create_list(n): my_list = [] for i in range(n): my_list.append(i) return my_list print(create_list(5)) |
15. A variable that exists outside a function has a scope inside the function body (Example 1) unless the function defines a variable of the same name (Example 2, and Example 3), e.g.:
Example 1:
1 2 3 4 5 |
var = 2 def mult_by_var(x): return x * var print(mult_by_var(7)) # outputs: 14 |
Example 2:
1 2 3 4 5 |
def mult(x): var = 5 return x * var print(mult(7)) # outputs: 35 |
Example 3:
1 2 3 4 5 6 |
def mult(x): var = 7 return x * var var = 3 print(mult(7)) # outputs: 49 |
16. A variable that exists inside a function has a scope inside the function body (Example 4), e.g.:
Example 4:
1 2 3 4 5 6 |
def adding(x): var = 7 return x + var print(adding(4)) # outputs: 11 print(var) # NameError |
17. You can use the global
keyword followed by a variable name to make the variable’s scope global, e.g.:
1 2 3 4 5 6 7 8 9 10 |
var = 2 print(var) # outputs: 2 def return_var(): global var var = 5 return var print(return_var()) # outputs: 5 print(var) # outputs: 5 |
The following example will shed some light on the issue:
1 2 3 4 5 6 7 8 9 10 |
def my_function(my_list_1): print("Print #1:", my_list_1) print("Print #2:", my_list_2) my_list_1 = [0, 1] print("Print #3:", my_list_1) print("Print #4:", my_list_2) my_list_2 = [2, 3] my_function(my_list_2) print("Print #5:", my_list_2) |
The code’s output is:
1 2 3 4 5 |
Print #1: [2, 3] Print #2: [2, 3] Print #3: [0, 1] Print #4: [2, 3] Print #5: [2, 3] |
It seems that the former rule still works.
Finally, can you see the difference in the example below:
1 2 3 4 5 6 7 8 9 10 |
def my_function(my_list_1): print("Print #1:", my_list_1) print("Print #2:", my_list_2) del my_list_1[0] # Pay attention to this line. print("Print #3:", my_list_1) print("Print #4:", my_list_2) my_list_2 = [2, 3] my_function(my_list_2) print("Print #5:", my_list_2) |
We don’t change the value of the parameter
my_list_1
(we already know it will not affect the argument), but instead modify the list identified by it.
The output may be surprising. Run the code and check:
1 2 3 4 5 |
Print #1: [2, 3] Print #2: [2, 3] Print #3: [3] Print #4: [3] Print #5: [3] |
Let’s explain it :
- if the argument is a list, then changing the value of the corresponding parameter doesn’t affect the list (remember: variables containing lists are stored in a different way than scalars),
- but if you change a list identified by the parameter (note: the list, not the parameter!), the list will reflect the change.
19. A function can call other functions or even itself. When a function calls itself, this situation is known as recursion, and the function which calls itself and contains a specified termination condition (i.e., the base case – a condition which doesn’t tell the function to make any further calls to that function) is called a recursive function.
20. You can use recursive functions in Python to write clean, elegant code, and divide it into smaller, organized chunks. On the other hand, you need to be very careful as it might be easy to make a mistake and create a function which never terminates. You also need to remember that recursive calls consume a lot of memory, and therefore may sometimes be inefficient.
When using recursion, you need to take all its advantages and disadvantages into consideration.
The factorial function is a classic example of how the concept of recursion can be put in practice:
1 2 3 4 5 6 7 8 |
def factorial_function(n): if n < 0: return None if n < 2: return 1 return n * factorial_function(n - 1) print(factorial(4)) # 4 * 3 * 2 * 1 = 2 |
Factorial without recursion:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def factorial_function(n): if n < 0: return None if n < 2: return 1 product = 1 for i in range(2, n + 1): product *= i return product for n in range(1, 6): # testing print(n, factorial_function(n)) |
Exercise 1
What is the output of the following snippet?
1 2 3 4 |
def intro(a="James Bond", b="Bond"): print("My name is", b + ".", a + ".") intro() |
# My name is Bond. James Bond.
Exercise 2
What is the output of the following snippet?
1 2 3 4 |
def intro(a="James Bond", b="Bond"): print("My name is", b + ".", a + ".") intro(b="Sean Connery") |
# My name is Sean Connery. James Bond.
Exercise 3
What is the output of the following snippet?
1 2 3 4 |
def intro(a, b="Bond"): print("My name is", b + ".", a + ".") intro("Susan") |
# My name is Bond. Susan.
Exercise 4
What is the output of the following snippet?
1 2 3 4 |
def add_numbers(a, b=2, c): print(a + b + c) add_numbers(a=1, c=3) |
# SyntaxError – a non-default argument (c) follows a default argument (b=2)
A non-default arguments must be before a default arguments.
Exercise 5
What is the output of the following snippet?
1 2 3 4 5 |
def hi(): return print("Hi!") hi() |
# the function will return an implicit None value
Exercise 6
What is the output of the following snippet?
1 2 3 4 5 6 7 8 9 |
def is_int(data): if type(data) == int: return True elif type(data) == float: return False print(is_int(5)) print(is_int(5.0)) print(is_int("5")) |
True
False
None
Exercise 7
What is the output of the following snippet?
1 2 3 4 5 6 7 8 |
def even_num_lst(ran): lst = [] for num in range(ran): if num % 2 == 0: lst.append(num) return lst print(even_num_lst(11)) |
[0, 2, 4, 6, 8, 10]
Exercise 8
What is the output of the following snippet?
1 2 3 4 5 6 7 8 9 |
def list_updater(lst): upd_list = [] for elem in lst: elem **= 2 upd_list.append(elem) return upd_list foo = [1, 2, 3, 4, 5] print(list_updater(foo)) |
[1, 4, 9, 16, 25]
Exercise 9
What is the output of the following snippet?
1 2 3 4 |
def hi(): return print("Hi!") hi() |
the function will return an implicit None
value
Exercise 10
What is the output of the following snippet?
1 2 3 4 5 6 7 8 9 |
def is_int(data): if type(data) == int: return True elif type(data) == float: return False print(is_int(5)) print(is_int(5.0)) print(is_int("5"))<code class="codep "> |
True
False
None
Exercise 11
What is the output of the following snippet?
1 2 3 4 5 6 7 8 |
def even_num_lst(ran): lst = [] for num in range(ran): if num % 2 == 0: lst.append(num) return lst print(even_num_lst(11)) |
[0, 2, 4, 6, 8, 10]
Exercise 12
What is the output of the following snippet?
1 2 3 4 5 6 7 |
def list_updater(lst): upd_list = [] for elem in lst: elem **= 2 upd_list.append(elem) return upd_list foo = [1, 2, 3, 4, 5] print(list_updater(foo)) |
[1, 4, 9, 16, 25]
Exercise 13
What will happen when you try to run the following code?
1 2 3 4 5 |
def message(): alt = 1 print("Hello, World!") print(alt) |
The NameError
exception will be thrown (NameError: name 'alt' is not defined
)
Exercise 14
What is the output of the following snippet?
1 2 3 4 5 6 7 |
a = 1 def fun(): a = 2 print(a) fun() print(a) |
2
1
Exercise 15
What is the output of the following snippet?
1 2 3 4 5 6 7 8 9 |
a = 1 def fun(): global a a = 2 print(a) fun() a = 3 print(a) |
2
3
Exercise 16
What is the output of the following snippet?
1 2 3 4 5 6 7 8 9 |
a = 1 def fun(): global a a = 2 print(a) a = 3 fun() print(a) |
2
2
Exercise 17
What will happen when you attempt to run the following snippet and why?
1 2 3 4 |
def factorial(n): return n * factorial(n - 1) print(factorial(4)) |
The factorial function has no termination condition (no base case) so Python will raise an exception (RecursionError: maximum recursion depth exceeded
)
Exercise 18
What is the output of the following snippet?
1 2 3 4 5 6 7 |
def fun(a): if a > 30: return 3 else: return a + fun(a + 3) print(fun(25)) |
56
Exercise 19
What is the output of the following snippet?
1 2 3 4 |
def fun(in=2, out=3): return in*out print (fun(3)) |
invalid syntax
in
is a keyword, you can not use in
as a name of parametr
Ex. 20
What is the output of the following code snippet?
1 2 3 4 5 6 7 8 |
x = 10 def fun(): global x if x >= 10: x += 100 print(x) |
10
The code does not call the fun() function. As a result, the program does not execute the body of the function and the content of the variable x does not change. Therefore, the print statement returns 10 on the screen.
Ex. 21
What is the output of the following code snippet?
1 2 3 4 |
def fun(x, y = 1): return x * y print(fun('3', 2)) |
Ex. 22
What is the output of the following code snippet?
1 2 3 4 5 6 |
def rectangle(b, h): perimeter = (b + h) * 2 area = b * h return b, h, area, perimeter print(rectangle(10, 3)[2]) |
Scenario
Your task is to write and test a function which takes one argument (a year) and returns True
if the year is a leap year, or False
otherwise.
To determine if a year is a leap year, we apply a simple rule: if the year is divisible by 4, it’s a leap year, except for end-of-century years, which must also be divisible by 400. For instance, the year 2000 was a leap year, while 1900 was not.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
def is_year_leap(year): x = year % 4 y = year % 100 z = year % 400 if (x == 0 and y != 0) or (y == 0 and z == 0): print (" x=",x, " y=", y, " z=", z, sep="") return True else: print (" x=",x, " y=", y, " z=", z, sep="") return False test_data = [1900, 2000, 2016, 1987] test_results = [False, True, True, False] for i in range(len(test_data)): yr = test_data[i] print(yr,"->",end="") result = is_year_leap(yr) if result == test_results[i]: print("OK") else: print("Failed") |
Scenario
Your task is to write and test a function which takes two arguments (a year and a month) and returns the number of days for the given month/year pair (while only February is sensitive to the year
value, your function should be universal).
The initial part of the function is ready. Now, convince the function to return None
if its arguments don’t make sense.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
def is_year_leap(year): x = year % 4 y = year % 100 z = year % 400 if (x == 0 and y != 0) or (y == 0 and z == 0): return True else: return False def days_in_month(year, month): even = month % 2 == 0 if month == 2: if is_year_leap(year): return 29 else: return 28 elif month <= 7: if even: return 30 else: return 31 elif month > 7: if even: return 31 else: return 30 test_years = [1900, 2000, 2016, 1987] test_months = [2, 2, 1, 11] test_results = [28, 29, 31, 30] for i in range(len(test_years)): yr = test_years[i] mo = test_months[i] print(yr, mo, "->", end="") result = days_in_month(yr, mo) if result == test_results[i]: print("OK") else: print("Failed") |
Scenario
Your task is to write and test a function which takes three arguments (a year, a month, and a day of the month) and returns the corresponding day of the year, or returns None
if any of the arguments is invalid.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
def is_year_leap(year): x = year % 4 y = year % 100 z = year % 400 if (x == 0 and y != 0) or (y == 0 and z == 0): return True else: return False def days_in_month(year, month): even = month % 2 == 0 if month == 2: if is_year_leap(year): return 29 else: return 28 elif month <= 7: if even: return 30 else: return 31 elif month > 7: if even: return 31 else: return 30 def day_of_year(year, month, day): count=0 for month in range (1,month): count+=days_in_month(year, month) #print(month, count) count+=day return count print(day_of_year(2001, 12, 31)) |
Scenario
A natural number is prime if it is greater than 1 and has no divisors other than 1 and itself.
Complicated? Not at all. For example, 8 isn’t a prime number, as you can divide it by 2 and 4 (we can’t use divisors equal to 1 and 8, as the definition prohibits this).
On the other hand, 7 is a prime number, as we can’t find any legal divisors for it.
Your task is to write a function checking whether a number is prime or not.
The function:
- is called
is_prime
; - takes one argument (the value to check)
- returns
True
if the argument is a prime number, andFalse
otherwise.
Hint: try to divide the argument by all subsequent values (starting from 2) and check the remainder – if it’s zero, your number cannot be a prime; think carefully about when you should stop the process.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def is_prime(num): if num > 1: flag = 1 for divider in range (2,10): if num % divider == 0 and num != divider and num>divider: # print(num, divider, sep=" -> ") flag=0 return flag for i in range(1, 100): if is_prime(i + 1): print(i + 1, end=" ") print() |
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
Fibonacci numbers
Are you familiar with Fibonacci numbers?
They are a sequence of integer numbers built using a very simple rule:
- the first element of the sequence is equal to one (Fib1 = 1)
- the second is also equal to one (Fib2 = 1)
- every subsequent number is the the_sum of the two preceding numbers:
(Fibi = Fibi-1 + Fibi-2)
Here are some of the first Fibonacci numbers:
fib_1 = 1
fib_2 = 1
fib_3 = 1 + 1 = 2
fib_4 = 1 + 2 = 3
fib_5 = 2 + 3 = 5
fib_6 = 3 + 5 = 8
fib_7 = 5 + 8 = 13
What do you think about implementing this as a function?
Let’s create our fib
function and test it. Here it is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
def fib(n): if n < 1: return None if n < 3: return 1 elem_1 = elem_2 = 1 the_sum = 0 for i in range(3, n + 1): the_sum = elem_1 + elem_2 elem_1, elem_2 = elem_2, the_sum return the_sum for n in range(1, 10): # testing print(n, "->", fib(n)) |
Recursion version:
1 2 3 4 5 6 |
def fib(n): if n < 1: return None if n < 3: return 1 return fib(n - 1) + fib(n - 2) |