In mathematics, a function is an object that accepts one or more inputs and produces one or more outputs. For example, , is a function that accepts a number and returns the square of that number. Functions in Python play a similar role, but are much more richer than their mathematical counterparts. Let us quickly convert the mathematical function, , into a Python function:
xxxxxxxxxx
31def f(x):
2 y = x ** 2
3 return y
The code given above is called the definition of function f
. def
is the keyword used to define functions. f
is the name of the function. x
is a parameter of the function. Lines 2 and 3 make up the body of the function and are indented. The body of a function is a collection of statements that describe what the function does. At line-3, the value stored in variable y
is returned. return
is the keyword used for this purpose.
If we run the above code, we will not get any output. Functions are not executed unless they are called. The following code demonstrates what a function call looks like:
xxxxxxxxxx
51def square(x):
2 y = x ** 2
3 return y
4
5print(square(2))
The output is:
xxxxxxxxxx
114
square(2)
is a function call. We use the name of the function, square
, and pass the number 2 as an argument to it. The x
in the function definition is called the parameter. The value that is passed to the function in the call is called the argument. This is a convention that we will follow throughout this lesson.
A visual representation of the terms we have defined so far is given below:
A mental model to understand functions:
We will look at a wide variety of function definitions. The focus will be on the syntactical aspects of function definitions.
xxxxxxxxxx
41# This function computes the area of a rectangle.
2# Length and breadth are the parameters
3def area(l, b):
4 return l * b
xxxxxxxxxx
21def foo():
2 return "I don't like arguments visiting me!"
xxxxxxxxxx
41def foo():
2 print("I don't like talking to the outside world!")
3
4foo()
When the code given above is executed, we get the following output:
xxxxxxxxxx
11I don't like talking to the outside world!
Note that we didn't have to type print(foo())
. We just had to call the function — foo()
— since it already has the print statement inside it. But what happens if we type print(foo())
? We get the following output:
xxxxxxxxxx
21I don't like talking to the outside world!
2None
If no explicit return statement is present in a function, None
is the default value returned by it. When the interpreter comes across the print(foo())
statement, first the function foo()
is evaluated. This results in the first line of the output. Since foo()
has no explicit return statement, it returns None
by default. That is why the second line in the output is None
.
xxxxxxxxxx
21def foo():
2 pass
pass
is a keyword in Python. When the interpreter comes across a pass
statement, it doesn't perform any computation and moves on to the next line. The reason this is minimal is because it has only those features that are absolutely essential for a function definition to be syntactically valid: function name and at least one statement in the body.
Such functions might seem useless at first sight, but they do have their place in programming. While writing a complex piece of code, a coder may realize that she needs to define a function to perform a specific task. But she may not know the exact details of the implementation or it may not be an urgent requirement. In such a scenario, she will add a minimal function like the one given above in her code and name it appropriately. Implementing this function will become a task on her to-do list and will be taken up as and when the need arises.
xxxxxxxxxx
31def foo():
2 return 1
3 return 2
foo()
will always return 1. Line-3 is redundant. An example of a function having multiple returns that are not redundant:
xxxxxxxxxx
81def evenOrOdd(n):
2 if n % 2 == 0:
3 return 'even'
4 else:
5 return 'odd'
6
7print(evenOrOdd(10))
8print(evenOrOdd(11))
The output is:
xxxxxxxxxx
21even
2odd
When evenOrOdd
is called with an even number as argument, the return statement in line-3 is executed. When the same function is called with an odd number as argument, the return statement in line-5 is executed.
xxxxxxxxxx
91# Accept only positive floating point numbers
2def bound(x):
3 lower = int(x)
4 upper = lower + 1
5 return lower, upper
6
7y = 7.3
8l, u = bound(y)
9print(f'{l} < {y} < {u}')
The exact mechanism of what happens here will become clear when we come to the lesson on tuples. In line-8, the first value returned by bound
is stored in l
and the second value returned by bound
is stored in u
.
xxxxxxxxxx
61##### Alarm! Wrong code snippet! #####
2print(f(5))
3
4def f(x):
5 return x ** 2
6##### Alarm! Wrong code snippet! #####
When the above code is executed, it throws a NameError
. Why does this happen? The Python interpreter executes the code from top to bottom. At line-2, f
is a name that the interpreter has never seen before and therefore it throws a NameError
. Recall that NameError
occurs when we try to reference a name that the interpreter has not seen before.
xxxxxxxxxx
61def square(a):
2 return a ** 2
3
4x, y, z = int(input()), int(input()), int(input())
5if square(x) + square(y) == square(z):
6 print(f'{x}, {y} and {z} form the sides of a right triangle with {z} as the hypotenuse')
xxxxxxxxxx
61##### Alarm! Wrong code snippet! #####
2def foo():
3 return True
4
5foo() = 1
6##### Alarm! Wrong code snippet! #####
The above code throws a SyntaxError
.
xxxxxxxxxx
111def foo():
2 print('I am inside foo')
3
4def bar():
5 print('I am inside bar')
6 print('I am going to call foo')
7 foo()
8
9print('I am outside both foo and bar')
10bar()
11print('I am outside both foo and bar')
xxxxxxxxxx
61def foo():
2 def bar():
3 print('bar is inside foo')
4 bar()
5
6foo()
Try calling bar()
outside foo
. What do you observe?
Consider the following function:
xxxxxxxxxx
31def square(x):
2 """Return the square of x."""
3 return x ** 2
The string immediately below the function definition is called a docstring. From the Python docs:
A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Such a docstring becomes the
__doc__
special attribute of that object.
Ignore unfamiliar terms such as "module" and "class". For now, it is sufficient to focus on functions. Adding the docstring to functions is a good practice. It may not be needed for simple and obvious functions like the one defined above. As the complexity of the functions you write increases, docstrings can be a life safer for other programmers reading your code.
The docstring associated with a given function can be accessed using the __doc__
attribute:
xxxxxxxxxx
11print(square.__doc__)
This gives 'Return the square of x.'
as output.