0% found this document useful (0 votes)
39 views45 pages

Course On Functions

This document discusses why functions are useful in programming and provides several examples of when and how to write your own functions. It notes that functions can make code more organized, reusable, and readable. Some key reasons to use functions include: (1) when a code fragment is repeated in multiple places, (2) when a piece of code becomes too large and complex, and (3) when dividing work among multiple programmers on a large project. The document also provides examples of built-in functions, user-defined functions, and how to define a basic function with parameters.

Uploaded by

Vartika Vashista
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
Download as docx, pdf, or txt
0% found this document useful (0 votes)
39 views45 pages

Course On Functions

This document discusses why functions are useful in programming and provides several examples of when and how to write your own functions. It notes that functions can make code more organized, reusable, and readable. Some key reasons to use functions include: (1) when a code fragment is repeated in multiple places, (2) when a piece of code becomes too large and complex, and (3) when dividing work among multiple programmers on a large project. The document also provides examples of built-in functions, user-defined functions, and how to define a basic function with parameters.

Uploaded by

Vartika Vashista
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1/ 45

Why do we need 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.

Where do the functions come from?


In general, functions come from at least three places:

 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.

How functions work


Look at the picture below:
It tries to show you the whole process:

 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?

print("We start here.")


message()
print("We end here.")

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?

No, it isn't. The error message will read:

NameError: name 'message' is not defined

output

Don't try to force Python to look for functions you didn't deliver at the right time.

The second catch sounds a little simpler:

You mustn't have a function and a variable of the same name.

The following snippet is erroneous:

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.

Look at the snippet:

print("We start here.")


def message():
print("Enter a value: ")

message()

print("We end here.")

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.

Open the sandbox, and try to do it yourself.

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.

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 (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:

def your_function(optional parameters):


# the body of the function

You can define a function which doesn't take any arguments, e.g.:

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:

def hello(name): # defining a function


print("Hello,", name) # body of the function

name = input("Enter your name: ")

hello(name) # calling the function

We'll tell you more about parametrized functions in the next section. Don't worry.
Exercise 1

The  input()  function is an example of a:

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

What will happen when you run the code below?

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:

 parameters live inside functions (this is their natural environment)


 arguments exist outside functions, and are carriers of values passed to corresponding parameters.

There is a clear and unambiguous frontier between these two worlds.

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.

We have to rebuild the  def  statement - this is how it looks now:

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.

Let's now improve the function's body:

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.

Failure to do so will cause an error.

Prev   Next

We use cookies to improve our service. By continuing to use the site, you agree to our privacy and cookie policies. ×

Parametrized functions: continued


Try to run the code in the editor.

This is what you'll see in the console:

TypeError: message() missing 1 required positional argument: 'number'

output

This looks better, for sure:

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 .

We have to make you sensitive to one important circumstance.


It's legal, and possible, to have a variable named the same as a function's parameter.

The snippet illustrates the phenomenon:

def message(number):
print("Enter a number:", number)

number = 1234
message(1)
print(number)

A situation like this activates a mechanism called shadowing:

 parameter  x  shadows any variable of the same name, but...


 ... only inside the function defining the parameter.

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

Parametrized functions: continued


A function can have as many parameters as you want, but the more parameters you have, the harder it is to memorize their roles
and purposes.
Let's modify the function - it has two parameters now:

def message(what, number):

print("Enter", what, "number", number)

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:

def message(what, number):

print("Enter", what, "number", number)

message("telephone", 11)

message("price", 5)

message("number", "number")

This is the output you're about to see:

Enter telephone number 11

Enter price number 5

Enter number 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)

# invoke the function


1

def message(what, number):

print("Enter", what, "number", number)

# invoke the function

 Console 


Prev   Next

Positional parameter passing


A technique which assigns the ith (first, second, and so on) argument to the ith (first, second, and so on) function
parameter is called positional parameter passing, while arguments passed in this way are named positional
arguments.

You've used it already, but Python can offer a lot more. We're going to tell you about it now.

def my_function(a, b, c):

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."

Incidentally, Hungarians do it in reverse order.

Let's implement that social custom in Python. The following function will be responsible for introducing somebody:

def introduction(first_name, last_name):

print("Hello, my name is", first_name, last_name)

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:

def introduction(first_name, last_name):

print("Hello, my name is", first_name, last_name)

introduction("Skywalker", "Luke")

introduction("Quick", "Jesse")

introduction("Kent", "Clark")

The output will look different. Can you guess it?


Run the code to see if you were right here, too. Are you surprised?

Can you make the function more culture-independent?

Keyword argument passing


Python offers another convention for passing arguments, where the meaning of the argument is dictated by its
name, not by its position - it's called keyword argument passing.

Take a look at the snippet:

def introduction(first_name, last_name):

print("Hello, my name is", first_name, last_name)

introduction(first_name = "James", last_name = "Bond")

introduction(last_name = "Skywalker", first_name = "Luke")

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.

Of course, you mustn't use a non-existent parameter name.

The following snippet will cause a runtime error:

def introduction(first_name, last_name):

print("Hello, my name is", first_name, last_name)

introduction(surname="Skywalker", first_name="Luke")
This is what Python will tell you:

TypeError: introduction() got an unexpected keyword argument 'surname'

output

Mixing positional and keyword arguments


You can mix both fashions if you want - there is only one unbreakable rule: you have to put positional arguments
before keyword arguments.

If you think for a moment, you'll certainly guess why.

To show you how it works, we'll use the following simple three-parameter function:

def adding(a, b, c):


print(a, "+", b, "+", c, "=", a + b + c)

Its purpose is to evaluate and present the sum of all its arguments.

The function, when invoked in the following way:

adding(1, 2, 3)

will output:

1 + 2 + 3 = 6

output

It was - as you may suspect - a pure example of positional argument passing.

Of course, you can replace such an invocation with a purely keyword variant, like this:
adding(c = 1, a = 2, b = 3)

Our program will output a line like this:

2 + 3 + 1 = 6

output

Note the order of the values.

Let's try to mix both styles now.

Look at the function invocation below:

adding(3, c = 1, b = 2)

Let's analyze it:

 the argument ( 3 ) for the  a  parameter is passed using the positional way;
 the arguments for  c  and  b  are specified as keyword ones.

This is what you'll see in the console:

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:

TypeError: adding() got multiple values for argument 'a'

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?

Parametrized functions - more details


It happens at times that a particular parameter's values are in use more often than others. Such arguments may have their default
(predefined) values taken into consideration when their corresponding arguments have been omitted.

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:

def introduction(first_name, last_name="Smith"):

print("Hello, my name is", first_name, last_name)

You only have to extend the parameter's name with the  =  sign, followed by the default value.

Let's invoke the function as usual:

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:

Hello, my name is Henry Smith

Hello, my name is William Smith

output

Test it.

You can go further if it's useful. Both parameters have their default values now, look at the code below:

def introduction(first_name="John", last_name="Smith"):

print("Hello, my name is", first_name, last_name)

This makes the following invocation absolutely valid:

introduction()
And this is the expected output:

Hello, my name is John Smith

output

If you use one keyword argument, the remaining one will take the default value:

introduction(last_name="Hopkins")

The output is:

Hello, my name is John 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.

An example of a one-parameter function:

def hi(name):
print("Hi,", name)

hi("Greg")

An example of a two-parameter function:

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:

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)

2. 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).
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 positional arguments mustn't follow keyword arguments. That's why if you try to run the
following snippet:

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 .

3. You can use the keyword argument passing technique to pre-define a value for a given argument:

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")

Exercise 1

What is the output of the following snippet?

def intro(a="James Bond", b="Bond"):


print("My name is", b + ".", a + ".")

intro()

Check

Exercise 2

What is the output of the following snippet?

def intro(a="James Bond", b="Bond"):


print("My name is", b + ".", a + ".")

intro(b="Sean Connery")

Check

Exercise 3

What is the output of the following snippet?

def intro(a, b="Bond"):


print("My name is", b + ".", a + ".")

intro("Susan")

Check

Exercise 4

What is the output of the following snippet?

def add_numbers(a, b=2, c):


print(a + b + c)

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.

Of course, functions - like their mathematical siblings - may have results.

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.

Let's consider the following function:

def happy_new_year(wishes = True):

print("Three...")

print("Two...")

print("One...")

if not wishes:

return

print("Happy New Year!")


When invoked without any arguments:

happy_new_year()

The function causes a little noise - the output will look like this:

Three...

Two...

One...

Happy New Year!

output

Providing  False  as an argument:

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

There are two consequences of using it:

 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.

Yes, we already know - this example isn't really sophisticated:

def boring_function():

return 123

x = boring_function()

print("The boring_function has returned its result. It's:", x)

The snippet writes the following text to the console:

The boring_function has returned its result. It's: 123

Let's investigate it for a while.


Analyze the figure

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.

The result may be freely used here, e.g., to be assigned to a variable.

It may also be completely ignored and lost without a trace.

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():

print("'Boredom Mode' ON.")

return 123

print("This lesson is interesting!")

boring_function()

print("This lesson is boring...")

The program produces the following output:


This lesson is interesting!

'Boredom Mode' ON.

This lesson is boring...

output

Is it punishable? Not at all.

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.

A few words about None


Let us introduce you to a very curious value (to be honest, a none value) named  None .

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.

For example, a snippet like this:

print(None + 2)

will cause a runtime error, described by the following diagnostic message:

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

output
Note:  None  is a keyword.

There are only two kinds of circumstances when  None  can be safely used:

 when you assign it to a variable (or return it as a function's result)


 when you compare it with a variable to diagnose its internal state.

Just like here:

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 .

Let's test it.

A few words about None: continued


Take a look at the code in the editor.

It's obvious that the  strangeFunction  function returns  True  when its argument is even.

What does it return otherwise?

We can use the following code to check it:

print(strange_function(2))

print(strange_function(1))

This is what we see in the console:

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.

Effects and results: lists and functions


There are two additional questions that should be answered here.

The first is: may a list be sent to a function as an argument?

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.

A function like this one here:

def list_sum(lst):

s = 0

for elem in lst:

s += elem

return s

and invoked like this:

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))

Python's response will be unequivocal:

TypeError: 'int' object is not iterable

output

This is caused by the fact that a single integer value mustn't be iterated through by the  for  loop.

Effects and results: lists and functions - continued


The second question is: may a list be a function result?

Yes, of course! Any entity recognizable by Python can be a function result.

Look at the code in the editor. The program's output will be like this:

[4, 3, 2, 1, 0]

output

Now you can write functions with and without results.

Let's dive a little deeper into the issues connected with variables in functions. This is essential for creating effective and
safe functions.

Functions and scopes


Let's start with a definition:

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:

NameError: name 'x' is not defined

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.

Functions and scopes: continued


Let's start by checking whether or not a variable created outside any function is visible inside the functions. In other
words, does a variable's name propagate into a function's body?

Look at the code in the editor. Our guinea pig is there.

The result of the test is positive - the code outputs:

Do I know that variable? 1


1

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.

Let's make a small change to the code:

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:

Do I know that variable? 2


1

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.

We can make the previous rule more precise and adequate:

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.

Functions and scopes: the global keyword


Hopefully, you should now have arrived at the following question: does this mean that a function is not able to modify a
variable defined outside it? This would create a lot of discomfort.

Fortunately, the answer is no.

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).

Such an effect is caused by a keyword named  global :

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).

Look at the code in the editor.

We've added  global  to the function.

The code now outputs:

Do I know that variable? 2

output

This should be sufficient evidence to show that the  global  keyword does what it promises.

How the function interacts with its arguments


Now let's find out how the function interacts with its arguments.

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?

Run the program and check.

def my_function(n):

print("I got", n)

n += 1
print("I have", n)

var = 1

my_function(var)

print(var)

The code's output is:

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?).

The following example will shed some light on the issue:

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:

Print #1: [2, 3]


Print #2: [2, 3]
Print #3: [0, 1]
Print #4: [2, 3]
Print #5: [2, 3]

output

It seems that the former rule still works.

Finally, can you see the difference in the example below:

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:

Print #1: [2, 3]


Print #2: [2, 3]
Print #3: [3]
Print #4: [3]
Print #5: [3]

output

Can you explain it?


Let's try:

 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.

Some simple functions: evaluating BMI and converting imperial units to


metric units
Look at the code in the editor. There are two things we need to pay attention to.

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

return weight / height ** 2

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.

What can be done for them?

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.

This is our helper function, named  lb_to_kg :

def lb_to_kg(lb):
return lb * 0.45359237

print(lb_to_kg(1))

The result of the test invocation looks good:

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 .

The function we've written is named  ft_and_inch_to_m :

def ft_and_inch_to_m(ft, inch):


return ft * 0.3048 + inch * 0.0254

print(ft_and_inch_to_m(1, 1))

The result of a quick test is:

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?

in  is a Python keyword - it cannot be used as a name.

Let's convert six feet into meters:


print(ft_and_inch_to_m(6, 0))

And this is the output:

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.

We've modified the code a bit:

def ft_and_inch_to_m(ft, inch = 0.0):


return ft * 0.3048 + inch * 0.0254

print(ft_and_inch_to_m(6))

Now the  inch  parameter has its default value equal to  0.0 .

The code produces the following output - this is what is expected:

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?

This is the code we have built:

def ft_and_inch_to_m(ft, inch = 0.0):


return ft * 0.3048 + inch * 0.0254

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

return weight / height ** 2

print(bmi(weight = lb_to_kg(176), height = ft_and_inch_to_m(5, 7)))

And the answer is:

27.565214082533313

output

Run the code and test it.

Some simple functions: recursion


There's one more thing we want to show you to make everything complete - it's recursion.

This term may describe many different concepts, but one of them is especially interesting - the one referring to
computer programming.

In this field, recursion is a technique where a function invokes itself.

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:

Fibi = Fibi-1 + Fibi-2

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)

The code is much clearer now.

But is it really safe? Does it entail any risk?

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.

The factorial has a second, recursive side too. Look:

n! = 1 × 2 × 3 × ... × n-1 × n

It's obvious that:

1 × 2 × 3 × ... × n-1 = (n-1)!

So, finally, the result is:

n! = (n-1)! × n

This is in fact a ready recipe for our new solution.

Here it is:

def factorial_function(n):
if n < 0:
return None
if n < 2:
return 1
return n * factorial_function(n - 1)

Does it work? Yes, it does. Try it for yourself.


Our short functional journey is almost over. The next section will take care of two curious Python data types: tuples and
dictionaries.

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.

does this mean that a function is not able to modify a variable defined outside it? This
would create a lot of discomfort.

Fortunately, the answer is no.

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).

Such an effect is caused by a keyword named global:

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).

You might also like