Functions in Python

What are Functions?

What are Functions?

A function is a named block of statements which perform one conceptual tasks.
A typical example for a function, in the real life, is a recipe in a cooking book.
The statements in the function are performed only after the function is called.
Functions help us to organise our code (break a task into sub-tasks)

Function Definition

Function Definition


			def function_name(opt_param_list):
				'''docstring'''
				statements
				return [expression]
		
def
A keyword that starts the function definition
function_name
Should be valid variable name.
opt_param_list
Optional. A list of function parameters. The braces around it are required, even if the list is empty.
'''docstring'''
Optional. A string for function documentation.
statements
block of statements, also called function body
return [expression]
Optional. Specify what the return value of the function should be.

The pass statement

Sometimes you need to define a function which body will be later written. But Python did not allows empty blocks!
In such cases, you can use the pass statement for function body:

			def my_fun():
			    pass
		

You can use the pass statement anywhere in your code, where you need a "do nothing" block!

simplest example


			def greet():
				"""Just prints hello"""

				print("Hello!")
		

Note, this code will not print anything if you try to run it as is.

This is just the function's definition. In order to execute the statements in it, the function should be called!

Function Call


Synonyms: Function Execution; Function Invocation

Function Call

The statements in the body of a function will be executed only when the function is called:


			function_name(opt_arg_list)
		

			# execute the greet() function:
			greet()
		

Note, the braces after the function name are required!

A function must be defined before you call it, or a NameError will be raised!


			# execute the greet() function:
			greet()

			# define greet() function:
			def greet():
			  print("Hello!")
			  print(5/2)

			# NameError: name 'greet' is not defined
		

function call - the right way:


			# define greet() function:
			def greet():
			  print("Hello!")

			# execute the greet() function:
			greet()

			# Hello!
		

Function Parameters

Function Parameters

A function is not very useful if it operates on same values

			def add():
				print(2+3)

			add()
			add()
			add()

			# 5
			# 5
			# 5
		
It will be better if we could do:

			# call add with different arguments:
			add(20, 22)
			add(123, 321)
			add(16, 10)

			# 42
			# 444
			# 26
		
You can define a function to use parameters by listing them in the braces.
Parameters names should be valid variable names.
When you call a function, you list in the braces the respective parameters values (called arguments)
Parameters are local function variables which get values when the function is called with the respective arguments.

Positional Arguments

Passing arguments values to parameters can be done by position. In this case, we speak about positional arguments/parameters:
first parameter gets the value of first argument
second parameter - the value of second argument
and so on...

Positional Arguments

The number of arguments passed must be equal to the number of parameters!

			def sub(x,y):
				""" subtracts y from x and prints the result """
				print(x - y)

			sub(99)
			# TypeError: sub() missing 1 required positional argument: 'y'

			sub(99, 3, 3)
			# TypeError: sub() takes 2 positional arguments but 3 were given
		

But you can use default parameters values.

Default Parameters Values

A default value to the "trailing" parameters can be defined, and that value will be used if no argument for that parameter is passed.

			def greet(name="Nobody"):
				""" greets a user """
				print("Hello", name)

			greet("Maria")
			greet()

			# Hello Maria
			# Hello Nobody

		

Default Parameters Values

Default parameters must follow the non-default parameters!


			def greet(msg="Hi", name):
				print("{} {}!".format(msg, name))

			greet("Maria")
			# SyntaxError: non-default argument follows default argument
		

			def greet(name, msg="Hi"):
				print("{} {}!".format(msg, name))

			greet("Maria")
			# Hi Maria!
		

Keyword (named) Arguments

If you explicitly state the parameter name in the argument list, that parameter will get its value, no matter of where it is positioned.

			def greet(msg, name):
				print("{} {}!".format(msg, name))

			greet(name="Maria",  msg="Hi")
		

Keyword (named) Arguments

Named arguments must follows the positional arguments!


			def greet(msg, name):
				print("{} {}!".format(msg, name))

			greet(name="Maria", "Hi")
			# SyntaxError: positional argument follows keyword argument
		

Variable Number of Positional Arguments (*args)

You can define a function with variable number of parameters if you unpack them in a tuple with the * (star) operator.

			def add(*args):
				add = 0
				for item in args:
					add += item

				print(add)

			add(1, 2)
			add(1, 2, 3)
			add(1, 2, 3, 4)
		

The name args can be any valid variable name, but its a convention to name the unpacked arguments with args

Variable Number of Positional Parameters (*args)

Variable arguments must follow the positional arguments:


			def add(p1, *args):
				print(p1, end=", ")
				print(args)

			add(1, 2)
			add(1, 2, 3)
			add(1, 2, 3, 4)

			# 1, (2,)
			# 1, (2, 3)
			# 1, (2, 3, 4)
		

Variable Number of Keyword Parameters

You can use ** (double stars) to unpack dictionary into keyword parameters.

			def menu_print(fruit, price):
				print("{:.<20s}{:.2f}".format(fruit,price))


			menu_print(**{
				"price": 2.5,
				"fruit": "apple"
			})

			# apple...............2.50
		

Unpacking arguments into positional parameters

You can unpack a list/tuple passed as argument into listed positional parameters with * operator

			def my_func(p1,p2,p3):
				print(p1, p2, p3)

			args = [1,2,3]
			my_func(*args)
			# 1 2 3
		

Note, that if you miss the star in my_func(*[1,2,3]), Python will assign the whole list [1,2,3] to p1, and the rest of parameters will receive no value. That will throw an error!

Function Return Values

Function Return Values

return statement

Functions in Python can return values, using the return statement:

			def f():
				statements
				return [expression]
		

			def add(x,y):
				return x+y

			print(add(2,4)**2)

			# 36
		

The return statement exits a function!


			def add(x,y):
				return x+y
				# next line will never be executed:
				print("After return")

			print(add(2,4))
		

Default return value

If a function did not have an explicit return statement, or if there is no expression after return keyword, then the function return value is None

			def foo():
				print("foo() was executed!")

			def bar():
				print("bar() was executed!")
				return

			print( foo() )
			print( bar() )

			# OUTPUT:
			# foo() was executed!
			# None
			# bar() was executed!
			# None
		

Variables Scope

Variables Scope

What?

The scope of a variables defines the place in our program, where a variable can be accessed (used, visible).

Local Scope

Names created inside a function are local to the function and are visible only inside that function

			def f1():
				y = 2
				print("y = {} inside f1(): ".format(y))

			f1()
			# y = 2 inside f1()

			print("y = {} outside f1(): ".format(y))
			# NameError: name 'y' is not defined
		

Global Scope

A name defined outside any functions is global to the file/module, and can be accessed after its definition from any place in that file/module.

			x = 10

			def f1():
				print("x = {} inside f1()".format(x))

			f1()
			# x = 10 inside f1()
			print("x = {} outside f1()".format(x))
			# x = 10 outside f1()

		

Names Resolution

When a name is defined inside function body, that name is created in function local scope (if the global keyword isnot used), even if the same name is already defined in global scope

				x = 10

				def f1():
					x = 99
					print("x = {} inside f1()".format(x))

				f1()
				# x = 99 inside f1()
				print("x = {} outside f1()".format(x))
				# x = 10 outside f1()
			

Variables with same name, defined in different scopes are considered as different variables!

Exercises

Task1: BMI with functions

Task

Write a program which will calculate a user BMI.
Split your logic into functions, and organise your program as given bellow:

			def get_user_data():
				"""retrieves user data from the command line

				Returns:
					[dictionary] of the form:
						{
							"name" : "user_name",
							"height": "user heigth in meters",
							"weight": "user weight in kilograms"
						}
				"""
				pass

			def calc_BMI(w,h):
				"""calculates the BMI

				Arguments:
					w {[float]} -- [weight]
					h {[float]} -- [height]

				Returns:
					[float] -- [calculated BMI = w / (h*h)]
				"""
				pass

			def calc_BMI_category(bmi):
				"""Calculates the BMI category

				Arguments:
					bmi {[float]} -- [the bmi number index]

				Returns:
					[string] -- [bmi category]
				"""
				pass

			def print_results(bmi_category):
				"""[Prints the BMI category to the user ]

				Arguments:
					bmi_category {[string]} -- []
				"""
				pass

			def cm_to_meters(cm):
				"""converts centimetres to meters

				Arguments:
					cm {[int]}

				Returns:
					[float]
				"""
				pass

			user_data = get_user_data()
			bmi = calc_BMI(user_data["weight"],user_data["height"] )
			bmi_category = calc_BMI_category(bmi)
			print_results(bmi_category)

		

Task2: Check user input

Improve the function get_user_data() defined in Task1, adding a check for valid user data:
User name must be at least 2 characters long
User's height must be in the range: [50 - 250]
User's weight must be in the range: [5 - 300]
If the user enters wrong data, ask for input again.
Define a separate function for each check, which will return True if the user input is valid, and False - otherwise.

Submition

You can write both of the tasks in one file: prefix_bmi_with_functions.py
where prefix is your name initials
For instance: iep_bmi_with_functions.py
Send it to progressbg.python.course@gmail.com

These slides are based on

customised version of

Hakimel's reveal.js

framework