Course On Functions
Course On Functions
You've come across functions many times so far, but the view on their merits that we have given you has been rather one-sided.
You've only invoked the functions by using them as tools to make life easier, and to simplify time-consuming and tedious tasks.
When you want some data to be printed on the console, you use print() . When you want to read the value of a variable, you
use input() , coupled with either int() or float() .
You've also made use of some methods, which are in fact functions, but declared in a very specific way.
Now you'll learn how to write your own functions, and how to use them. We'll write several functions together, from the very
simple to the rather complex, which will require your focus and attention.
It often happens that a particular piece of code is repeated many times in your program. It's repeated either literally, or with only
a few minor modifications, consisting of the use of other variables in the same algorithm. It also happens that a programmer cannot
resist simplifying the work, and begins to clone such pieces of code using the clipboard and copy-paste operations.
It could end up as greatly frustrating when suddenly it turns out that there was an error in the cloned code. The programmer will
have a lot of drudgery to find all the places that need corrections. There's also a high risk of the corrections causing errors.
We can now define the first condition which can help you decide when to start writing your own functions: if a particular
fragment of the code begins to appear in more than one place, consider the possibility of isolating it in the form of a
function invoked from the points where the original code was placed before.
It may happen that the algorithm you're going to implement is so complex that your code begins to grow in an uncontrolled
manner, and suddenly you notice that you're not able to navigate through it so easily anymore.
You can try to cope with the issue by commenting the code extensively, but soon you find that this dramatically worsens your
situation - too many comments make the code larger and harder to read. Some say that a well-written function should be
viewed entirely in one glance.
A good and attentive developer divides the code (or more accurately: the problem) into well-isolated pieces, and encodes each of
them in the form of a function.
This considerably simplifies the work of the program, because each piece of code can be encoded separately, and tested separately.
The process described here is often called decomposition.
We can now state the second condition: if a piece of code becomes so large that reading and understating it may cause a
problem, consider dividing it into separate, smaller problems, and implement each of them in the form of a separate
function.
This decomposition continues until you get a set of short functions, easy to understand and test.
Decomposition
It often happens that the problem is so large and complex that it cannot be assigned to a single developer, and a team of
developers have to work on it. The problem must be split between several developers in a way that ensures their efficient and
seamless cooperation.
It seems inconceivable that more than one programmer should write the same piece of code at the same time, so the job has to be
dispersed among all the team members.
This kind of decomposition has a different purpose to the one described previously - it's not only about sharing the work, but also
about sharing the responsibility among many developers.
Each of them writes a clearly defined and described set of functions, which when combined into the module (we'll tell you about
this a bit later) will give the final product.
This leads us directly to the third condition: if you're going to divide the work among multiple programmers, decompose the
problem to allow the product to be implemented as a set of separately written functions packed together in different
modules.
from Python itself - numerous functions (like print() ) are an integral part of Python, and are always available without
any additional effort on behalf of the programmer; we call these functions built-in functions;
from Python's preinstalled modules - a lot of functions, very useful ones, but used significantly less often than built-in
ones, are available in a number of modules installed together with Python; the use of these functions requires some
additional steps from the programmer in order to make them fully accessible (we'll tell you about this in a while);
directly from your code - you can write your own functions, place them inside your code, and use them freely;
there is one other possibility, but it's connected with classes, so we'll omit it for now.
when you invoke a function, Python remembers the place where it happened and jumps into the invoked function;
the body of the function is then executed;
reaching the end of the function forces Python to return to the place directly after the point of invocation.
There are two, very important, catches. Here's the first of them:
You mustn't invoke a function which is not known at the moment of invocation.
Remember - Python reads your code from top to bottom. It's not going to look ahead in order to find a function you forgot to put in
the right place ("right" means "before invocation".)
We've inserted an error into this code - can you see the difference?
def message():
print("Enter a value: ")
We've moved the function to the end of the code. Is Python able to find it when the execution reaches the invocation?
output
Don't try to force Python to look for functions you didn't deliver at the right time.
def message():
print("Enter a value: ")
message = 1
Assigning a value to the name message causes Python to forget its previous role. The function named message becomes
unavailable.
Fortunately, you're free to mix your code with functions - you're not obliged to put all your functions at the top of your source
file.
message()
It may look strange, but it's completely correct, and works as intended.
Let's return to our primary example, and employ the function for the right job, like here:
def message():
print("Enter a value: ")
message()
a = int(input())
message()
b = int(input())
message()
c = int(input())
Modifying the prompting message is now easy and clear - you can do it by changing the code in just one place - inside the
function's body.
Prev Next
We use cookies to improve our service. By continuing to use the site, you agree to our privacy and cookie policies. ×
Key takeaways
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.
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 (you'll learn about them in the Python Essentials 2 course)
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 (you'll learn about them in the Python Essentials 2 course.)
3. You can define your own function using the def keyword and the following syntax:
You can define a function which doesn't take any arguments, e.g.:
You can define a function which takes arguments, too, just like the one-parameter function below:
We'll tell you more about parametrized functions in the next section. Don't worry.
Exercise 1
a) user-defined function
b) built-in function
Check
Exercise 2
What happens when you try to invoke a function before you define it? Example:
hi()
def hi():
print("hi!")
Check
Exercise 3
def hi():
print("hi")
hi(5)
Check
Prev Next
We use cookies to improve our service. By continuing to use the site, you agree to our privacy and cookie polici
Parameterized functions
The function's full power reveals itself when it can be equipped with an interface that is able to accept data provided by the
invoker. Such data can modify the function's behavior, making it more flexible and adaptable to changing conditions.
A parameter is actually a variable, but there are two important factors that make parameters different and special:
parameters exist only inside functions in which they have been defined, and the only place where the parameter can be
defined is a space between a pair of parentheses in the def statement;
assigning a value to the parameter is done at the time of the function's invocation, by specifying the corresponding
argument.
def function(parameter):
###
Don't forget:
Let's enrich the function above with just one parameter - we're going to use it to show the user the number of a value the function
asks for.
def message(number):
###
The definition specifies that our function operates on just one parameter named number . You can use it as an ordinary variable,
but only inside the function - it isn't visible anywhere else.
def message(number):
print("Enter a number:", number)
We've made use of the parameter. Note: we haven't assigned the parameter with any value. Is it correct?
Yes, it is.
A value for the parameter will arrive from the function's environment.
Remember: specifying one or more parameters in a function's definition is also a requirement, and you have to fulfil it during
invocation. You must provide as many arguments as there are defined parameters.
Prev Next
We use cookies to improve our service. By continuing to use the site, you agree to our privacy and cookie policies. ×
output
def message(number):
print("Enter a number:", number)
message(1)
Moreover, it behaves better. The code will produce the following output:
Enter a number: 1
output
Can you see how it works? The value of the argument used during invocation ( 1 ) has been passed into the function,
setting the initial value of the parameter named number .
def message(number):
print("Enter a number:", number)
number = 1234
message(1)
print(number)
The parameter named number is a completely different entity from the variable named number .
This means that the snippet above will produce the following output:
Enter a number: 1
1234
output
This also means that invoking the function will require two arguments.
The first new parameter is intended to carry the name of the desired value.
Here it is:
message("telephone", 11)
message("price", 5)
message("number", "number")
output
Run the code, modify it, add more parameters, and see how this affects the output.
Sandbox
Code
def message(what, number):
print("Enter", what, "number", number)
Console
Prev Next
You've used it already, but Python can offer a lot more. We're going to tell you about it now.
print(a, b, c)
my_function(1, 2, 3)
Note: positional parameter passing is intuitively used by people in many social occasions. For example, it may be
generally accepted that when we introduce ourselves we mention our first name(s) before our last name, e.g., "My
name's John Doe."
Let's implement that social custom in Python. The following function will be responsible for introducing somebody:
introduction("Luke", "Skywalker")
introduction("Jesse", "Quick")
introduction("Clark", "Kent")
Can you guess the output? Run the code and find out if you were right.
Now imagine that the same function is being used in Hungary. In this case, the code would look like this:
introduction("Skywalker", "Luke")
introduction("Quick", "Jesse")
introduction("Kent", "Clark")
The concept is clear - the values passed to the parameters are preceded by the target parameters' names, followed by
the = sign.
The position doesn't matter here - each argument's value knows its destination on the basis of the name used.
You should be able to predict the output. Run the code to check if you were right.
introduction(surname="Skywalker", first_name="Luke")
This is what Python will tell you:
output
To show you how it works, we'll use the following simple three-parameter function:
Its purpose is to evaluate and present the sum of all its arguments.
adding(1, 2, 3)
will output:
1 + 2 + 3 = 6
output
Of course, you can replace such an invocation with a purely keyword variant, like this:
adding(c = 1, a = 2, b = 3)
2 + 3 + 1 = 6
output
adding(3, c = 1, b = 2)
the argument ( 3 ) for the a parameter is passed using the positional way;
the arguments for c and b are specified as keyword ones.
3 + 2 + 1 = 6
output
Be careful, and beware of mistakes. If you try to pass more than one value to one argument, all you'll get is a runtime
error.
Look at the invocation below - it seems that we've tried to set a twice:
adding(3, a = 1, b = 2)
Python's response:
output
Look at the snipet below. A code like this is fully correct, but it doesn't make much sense:
adding(4, 3, c = 2)
Everything is right, but leaving in just one keyword argument looks a bit weird - what do you think?
They say that the most popular English last name is Smith. Let's try to take this into account.
The default parameter's value is set using clear and pictorial syntax:
You only have to extend the parameter's name with the = sign, followed by the default value.
introduction("James", "Doe")
Can you guess the output of the program? Run it and check if you were right.
And? Everything looks the same, but when you invoke the function in a way that looks a bit suspicious at first sight, like this:
introduction("Henry")
or this:
introduction(first_name="William")
there will be no error, and both invocations will succeed, while the console will show the following output:
output
Test it.
You can go further if it's useful. Both parameters have their default values now, look at the code below:
introduction()
And this is the expected output:
output
If you use one keyword argument, the remaining one will take the default value:
introduction(last_name="Hopkins")
output
Test it.
Congratulations - you have just learned the basic ways of communicating with functions.
Key takeaways
1. You can pass information to functions by using parameters. Your functions can have as many parameters as you need.
def hi(name):
print("Hi,", name)
hi("Greg")
hi_all("Sebastian", "Konrad")
s = input("Street: ")
p_c = input("Postal Code: ")
c = input("City: ")
address(s, c, p_c)
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).
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)
Ex. 3
def subtra(a, b):
print(a - b)
It's important to remember that positional arguments mustn't follow keyword arguments. That's why if you try to run the
following snippet:
3. You can use the keyword argument passing technique to pre-define a value for a given argument:
Exercise 1
intro()
Check
Exercise 2
intro(b="Sean Connery")
Check
Exercise 3
intro("Susan")
Check
Exercise 4
add_numbers(a=1, c=3)
Check
Effects and results: the return instruction
All the previously presented functions have some kind of effect - they produce some text and send it to the console.
To get functions to return a value (but not only for this purpose) you use the return instruction.
This word gives you a full picture of its capabilities. Note: it's a Python keyword.
The return instruction has two different variants - let's consider them separately.
return without an expression
The first consists of the keyword itself, without anything following it.
When used inside a function, it causes the immediate termination of the function's execution, and an instant return (hence the
name) to the point of invocation.
Note: if a function is not intended to produce a result, using the return instruction is not obligatory - it will be executed
implicitly at the end of the function.
Anyway, you can use it to terminate a function's activities on demand, before the control reaches the function's last line.
print("Three...")
print("Two...")
print("One...")
if not wishes:
return
happy_new_year()
The function causes a little noise - the output will look like this:
Three...
Two...
One...
output
happy_new_year(False)
will modify the function's behavior - the return instruction will cause its termination just before the wishes - this is the updated
output:
Three...
Two...
One...
output
return with an expression
The second return variant is extended with an expression:
def function():
return expression
it causes the immediate termination of the function's execution (nothing new compared to the first variant)
moreover, the function will evaluate the expression's value and will return (hence the name once again) it as the
function's result.
def boring_function():
return 123
x = boring_function()
below:
The return instruction, enriched with the expression (the expression is very simple here), "transports" the expression's value to
the place where the function has been invoked.
Note, we're not being too polite here - the function returns a value, and we ignore it (we don't use it in any way):
def boring_function():
return 123
boring_function()
output
The only disadvantage is that the result has been irretrievably lost.
Don't forget:
you are always allowed to ignore the function's result, and be satisfied with the function's effect (if the function has any)
if a function is intended to return a useful result, it must contain the second variant of the return instruction.
Wait a minute - does this mean that there are useless results, too? Yes - in some sense.
Its data doesn't represent any reasonable value - actually, it's not a value at all; hence, it mustn't take part in any expressions.
print(None + 2)
output
Note: None is a keyword.
There are only two kinds of circumstances when None can be safely used:
value = None
if value is None:
print("Sorry, you don't carry any value")
Don't forget this: if a function doesn't return a certain value using a return expression clause, it is assumed that it implicitly
returns None .
It's obvious that the strangeFunction function returns True when its argument is even.
print(strange_function(2))
print(strange_function(1))
True
None
output
Don't be surprised next time you see None as a function result - it may be the symptom of a subtle mistake inside the
function.
Of course it may! Any entity recognizable by Python can play the role of a function argument, although it has to be
assured that the function is able to cope with it.
So, if you pass a list to a function, the function has to handle it like a list.
def list_sum(lst):
s = 0
s += elem
return s
print(list_sum([5, 4, 3]))
will return 12 as a result, but you should expect problems if you invoke it in this risky way:
print(list_sum(5))
output
This is caused by the fact that a single integer value mustn't be iterated through by the for loop.
Look at the code in the editor. The program's output will be like this:
[4, 3, 2, 1, 0]
output
Let's dive a little deeper into the issues connected with variables in functions. This is essential for creating effective and
safe functions.
The scope of a name (e.g., a variable name) is the part of a code where the name is properly recognizable.
For example, the scope of a function's parameter is the function itself. The parameter is inaccessible outside the
function.
Let's check it. Look at the code in the editor. What will happen when you run it?
The program will fail when run. The error message will read:
output
This is to be expected.
We're going to conduct some experiments with you to show you how Python constructs scopes, and how you can use
its habits to your benefit.
output
The answer is: a variable existing outside a function has a scope inside the functions' bodies.
This rule has a very important exception. Let's try to find it.
def my_function():
var = 2
print("Do I know that variable?", var)
var = 1
my_function()
print(var)
The result has changed, too - the code produces a slightly different output now:
output
What's happened?
the var variable created inside the function is not the same as when defined outside it - it seems that there two
different variables of the same name;
moreover, the function's variable shadows the variable coming from the outside world.
A variable existing outside a function has a scope inside the functions' bodies, excluding those of them which
define a variable of the same name.
It also means that the scope of a variable existing outside a function is supported only when getting its
value (reading). Assigning a value forces the creation of the function's own variable.
Make sure you understand this well and carry out your own experiments.
There's a special Python method which can extend a variable's scope in a way which includes the functions'
bodies (even if you want not only to read the values, but also to modify them).
global name
global name1, name2, ...
Using this keyword inside a function with the name (or names separated with commas) of a variable(s), forces Python
to refrain from creating a new variable inside the function - the one accessible from outside will be used instead.
In other words, this name becomes global (it has a global scope, and it doesn't matter whether it's the subject of read
or assign).
output
This should be sufficient evidence to show that the global keyword does what it promises.
The code in the editor should teach you something. As you can see, the function changes the value of its parameter.
Does the change affect the argument?
def my_function(n):
print("I got", n)
n += 1
print("I have", n)
var = 1
my_function(var)
print(var)
I got 1
I have 2
1
output
The conclusion is obvious - changing the parameter's value doesn't propagate outside the function (in any case,
not when the variable is a scalar, like in the example).
This also means that a function receives the argument's value, not the argument itself. This is true for scalars.
Is it worth checking how it works with lists (do you recall the peculiarities of assigning list slices versus assigning lists
as a whole?).
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:
output
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.
output
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.
First, the test invocation ensures that the protection works properly - the output is:
None
def bmi(weight, height):
if height < 1.0 or height > 2.5 or \
weight < 20 or weight > 200:
return None
print(bmi(352.5, 1.65))
Second, take a look at the way the backslash ( \ ) symbol is used. If you use it in Python code and end a line with it, it
will tell Python to continue the line of code in the next line of code.
It can be particularly useful when you have to deal with long lines of code and you'd like to improve code readability.
Okay, but there's something we omitted too easily - the imperial measurements. This function is not too useful for
people accustomed to pounds, feet and inches.
We can write two simple functions to convert imperial units to metric ones. Let's start with pounds.
It is a well-known fact that 1 lb = 0.45359237 kg . We'll use this in our new function.
def lb_to_kg(lb):
return lb * 0.45359237
print(lb_to_kg(1))
0.45359237
output
And now it's time for feet and inches: 1 ft = 0.3048 m , and 1 in = 2.54 cm = 0.0254 m .
print(ft_and_inch_to_m(1, 1))
0.3302
output
It looks as expected.
Note: we wanted to name the second parameter just in , not inch , but we couldn't. Do you know why?
1.8288000000000002
output
It's quite possible that sometimes you may want to use just feet without inches. Will Python help you? Of course it will.
print(ft_and_inch_to_m(6))
Now the inch parameter has its default value equal to 0.0 .
1.8288000000000002
output
Finally, the code is able to answer the question: what is the BMI of a person 5'7" tall and weighing 176 lbs?
def lb_to_kg(lb):
return lb * 0.45359237
def bmi(weight, height):
if height < 1.0 or height > 2.5 or weight < 20 or weight > 200:
return None
27.565214082533313
output
This term may describe many different concepts, but one of them is especially interesting - the one referring to
computer programming.
These two cases seem to be the best to illustrate the phenomenon - factorials and Fibonacci numbers. Especially the
latter.
The Fibonacci numbers definition is a clear example of recursion. We already told you that:
The definition of the ith number refers to the i-1 number, and so on, till you reach the first two.
Can it be used in the code? Yes, it can. It can also make the code shorter and clearer.
The second version of our fib() function makes direct use of this definition:
def fib(n):
if n < 1:
return None
if n < 3:
return 1
return fib(n - 1) + fib(n - 2)
Yes, there is a little risk indeed. If you forget to consider the conditions which can stop the chain of recursive
invocations, the program may enter an infinite loop. You have to be careful.
n! = 1 × 2 × 3 × ... × n-1 × n
n! = (n-1)! × n
Here it is:
def factorial_function(n):
if n < 0:
return None
if n < 2:
return 1
return n * factorial_function(n - 1)
does this mean that a function is not able to modify a variable defined outside it? This
would create a lot of discomfort.
There's a special Python method which can extend a variable's scope in a way which
includes the functions' bodies (even if you want not only to read the values, but also to
modify them).
global name
global name1, name2, ...
Using this keyword inside a function with the name (or names separated with commas) of
a variable(s), forces Python to refrain from creating a new variable inside the function -
the one accessible from outside will be used instead.
In other words, this name becomes global (it has a global scope, and it doesn't matter
whether it's the subject of read or assign).