Skip to content

Functions

In this lesson we'll learn about how to use built-in functions, methods and writing our own functions.

Functions

Python has many built-in functions. We've seen some of the already.

print("Python")

n = len("Python")
print(n)

In the above example, print and len are built-in functions. The built-in functions are available to all programs.

For example, abs is another built-in function that computes the absolute value of a number.

a = abs(3)
b = abs(-3)
print(a, b)

You may have noticed that Python doesn't allow operations on incompatible datatypes.

>>> 1 + '2'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

If you want to add an integer to a string, you need convert the integer to a string or the string to an integer. The built-in functions int and str come handy in this case.

x = 1 + int('2')
y = str(1) + '2'

print(x, y)

Example: Counting the number of digits in a number

How to count the number of digits in a number?

The following example shows some really big numbers. What does it take to count the number of digits in them?

print(12345)
print(2**100)
print(2**1000)

We know that we can use the len function to compute the length of a string, but here we have an integer, not a string. So we need to convert the integer to a string first using the str function and then apply len.

n = len(str(2**1000))
print(n)

Writing Custom Functions

While using built-in functions is handy in a lot of cases, sometimes we want to define our own functions to avoid repeating the same code again and again.

Let's define a function to compute square of a number.

def square(x):
    return x*x

n = square(4)
print(n)

Notice that the body of the function is indented, typically by 4 spaces. Python uses indentation to identify the code that is part of the function body.

We could write the same function as follows:

def square(x):
    y = x*x
    return y

n = square(4)
print(n)

Often beginners get confused between print and return.

Consider the following two functions.

def square1(x):
    return x*x

def square2(x):
    print(x*x)

print(square1(4))
square2(4)

The function square1 is returning a value where as the function square2 is printing the value instead of returning. When we run the program, both functions seems to be working exactly in the same way. Is that really true? Can we find a case where they work quite differently?

Let's say we want to add the number 1 to the square. It is possible to do it using square1 as it is returning the value, which can be used in other computations.

def square1(x):
    return x*x

n = square1(4) + 1
print(n)

It is not possible to do that same with square2 without changing the definition of that function because it is printing the value directly and not returning anything back to the caller.

Functions are values too!

Functions are first-class objects in Python. Funtions are like any other variables with the value being of datatype function. They can be assigned to other variables, or passed as arguments to other functions or can even returned from functions.

def square(n):
    return n*n

print(square(4))
print(square)

f = square
print(f(4))

If you see the second print statement, it is printing the value of the function itself. Don't worry about the hexadecimal value after the function name. It is printing that it is a function with a particular name.

In the last part of the program, we've assigned to the value of square to a variable f, which means f has exactly the same value as of square, which is the square function. So we were able to call f like a function in the last line.

While this may sound absurd. Why would anyone want to do it?

Well, there are many use cases and you'll be surprised how powerful this feature is.

Let's say we want to compute sum of squares of two numbers.

def square(n):
    return n*n

def sum_of_squares(x, y):
    return square(x) + square(y)

n = sum_of_squares(3, 4)
print(n)

What if I want to do the same thing for cubes instead of squares? One way to do that would be to copy the code and replace square with cube.

def cube(n):
    return n*n*n

def sum_of_cubes(x, y):
    return cube(x) + cube(y)

n = sum_of_cubes(3, 4)
print(n)

If you compare the above two programs, they are almost identical. Can be generalize both sum_of_squares and sum_of_cubes into a single function?

def sumof(f, x, y):
    # print("sumof", f, x, y)
    return f(x) + f(y)

def square(n):
    return n*n

def cube(n):
    return n*n*n

print(sumof(square, 3, 4))
print(sumof(cube, 3, 4))

print(sumof(len, 'hello', 'python'))

As you can see the sumof function is taking three arguments. The first one is a function and the remaining two are the arguments that are passed to that function.

If we call sumof with square as first argument and pass two numbers as other arguments, it would call the function square for each of those numbers, adds the results and return it back. The same applies when we pass cube as the first argument.

This feature of passing functions as arguments to other functions is so useful that there are many built-in functions that accept functions as arguments.

For example, Python has a built-in function max to compute maximum of two values or a list of values.

n = max(3, 4)
print(n)

n = max([3, 4, 5, 2])
print(n)

What do you think would be the value of max(['one', 'two', 'three', 'four', 'five'])?

x = max(['one', 'two', 'three', 'four', 'five'])
print(x)

Why is this giving two? Why not three? Why not five?

The way it works is it compares the words by the dictionary ordering and two comes at the end in the dictionary ordering.

What if we want to find the longest word instead?

numbers = ['one', 'two', 'three', 'four', 'five']
n = max(numbers, key=len)
print(n)

The built-in function max accepts an optional argument key, which is a function when passed, used to compute the value used for comparison.

The following example demonstrates what really happens. We are using a function mylen instead of len, which works like len, but prints the value it is called with.

def mylen(x):
    print("mylen", x)
    return len(x)

numbers = ['one', 'two', 'three', 'four', 'five']
n = max(numbers, key=mylen)
print(n)

As you can see, the mylen function is called with every element of the list numbers as argument, which is done by the max function when computing the maximum.

Methods

Methods are special kind of functions that work on an object.

Let's look at the following example:

x = "Hello"
print(x.upper())
print(x.lower())

Notice that we are calling upper as x.upper(). In this case, upper is a method called on object x, which is a string. The upper method is defined in the str type and is available to all values of that type.

Let's look at some more methods available on strings.

n = "mathematics".count("mat")
print(n)

x = "mathematics".replace("mat", "rat")
print(x)

As you can see, the count method computes the number of occurances of a substring in a string and the replace method returns a new string after replacing the occurances of a substring specified as the first argument with the second one.

Let's look at some more interesting methods on strings.

The split method splits a string on a delimiter.

sentence = "Anything that can go wrong, will go wrong"
words = sentence.split() # split on any white space
print(words)

parts = sentence.split(",")
print(parts)

When the split method is called without any arguments, it considers any whitespace (including tab and new line) as delimiter.

We've seen how to split a string. Now, let's see how to join them back.

s1 = " ".join(["a", "b", "c"])
print(s1)

s2 = "-".join(["a", "b", "c"])
print(s2)

s3 = "::".join(["a", "b", "c"])
print(s3)

The join method is called on the delimiter and it takes a list of strings as argument and returns the string generated by combining all the elements of the list with the delimiter.