- What is a function?
- Function basics
- Returning values
- Function arguments
- Function scope
- Keyword arguments
- Composition with functions
Now that we’ve learned about Python’s built-in functions, let’s see how we can write our own functions.
What is a function?
Functions are modular pieces of logic. They help to make code less redundant, more organized, more flexible and easier to maintain.
Typically, a function is defined once, and used several times in a program. A function can also live in one place (for example in an external module) and be used somewhere else.
Functions are a Good Thing™. They help us to follow DRY (Don’t Repeat Yourself) and KISS (Keep It Simple Stupid) principles. They help us to think more clearly and work more efficiently.
A function should not try to do many different things at once.
Ideally, a function should do one thing really well, and nothing else (see Single responsability principle). This makes it easier to reuse this function in another context, to solve a similar problem.
Name your functions wisely.
Think of functions as the ‘verbs’ in your code. Choose clear descriptive names.
To use a function we need to do two things:
- define the function
- call the function
Defining a function
A function is defined with the
def statement. A simple function definition looks like this:
def sayHi(): print("Hi!")
In the first line we have the name of the function (
sayHi) and a pair of parentheses (which in this case is empty).
After that we have the body of the function, which contains the code that gets executed every time the function is called.
The body of a function cannot be completely empty. If we wish to create a function that does nothing at all, we need to use the
pass statement in its body:
def doNothing(): pass
Calling a function
Once a function has been defined, we can call it anywhere in our program, as many times as we want.
To call a function, simply write its name followed by a pair of parentheses:
If we wish to call this function several times, we can simply put it in a loop:
for i in range(4): sayHi()
>>> Hi! >>> Hi! >>> Hi! >>> Hi!
And at anytime, we can edit our function definition, so it outputs a different message:
def sayHi(): print("hello there") for i in range(4): sayHi()
>>> hello there >>> hello there >>> hello there >>> hello there
The functions we’ve seen above do something: they print a message. But they don’t return any value.
Functions that only do something
Let’s have a look at another example:
m = sayHi()
Here we are calling the same function again, but we are assigning the value it returns to a variable. When we call the function, it prints the message as previously. When we print the value stored in the variable
m, we get
None – because the function didn’t return a value.
Functions that return one or more things
Now let’s write another function that doesn’t do anything, but which returns a value. Instead of printing a string, our function will
def makeHi(): return 'Hi!'
When we call this function, no message gets printed. But it returns a value, which we can store in a variable to use later:
m = makeHi() print(m)
Of course, our functions can also do both things at once: do something and return a value:
def makeSpam(): # do something print('making spam...') # return a value return 'hmmm tasty spam!' mySpam = makeSpam()
>>> making spam...
>>> hmmm tasty spam!
A function can also return more than one value (a tuple of values):
def makeColor(): r = 1.0 g = 0.5 b = 0.0 return r, g, b c = makeColor() print(c)
>>> (1.0, 0.5, 0.0)
>>> <type 'tuple'>
So far all our simple functions have been doing their work without any input from the “outside world” – these functions take no arguments or parameters.
But we can write functions that take one or more arguments as input, and use these to do something else.
Let’s build on a previous example. We want to make a function that says “Hi” to someone, so we need to find a way to inform this person’s name to the function. We do this by declaring a variable name between the parentheses in the function definition:
def sayHello(name): print("hello", name)
Now this function can only do its work if we give it a name. It requires an argument. If we don’t pass any, it will throw an error:
>>> Traceback (most recent call last): >>> File "<untitled>", line 4, in <module> >>> TypeError: sayHello() takes exactly 1 argument (0 given)
The error message is clear about the problem: we haven’t given any argument to the function. Let’s try again:
>>> hello Waldemar
>>> hello Ahmed
>>> hello Leoni
Functions can take more than one argument, of course. Here’s an example of a function that takes three arguments:
def sayHello(firstName, lastName, title): print("hello", title, firstName, lastName) sayHello('John', 'Smith', 'Mr.')
>>> hello Mr. John Smith
sayHello('Marie', 'Curie', 'Mrs.')
>>> hello Mrs. Marie Curie
Everything that goes into a function’s body is not accessible to the ‘outside world’ – the part of the program that is outside the function definition:
# global scope def myFunc(): # function scope pass # global scope
Beginners often struggle to understand this topic, so we recommend you to pay extra attention.
So, function scope is isolated from the global scope. Let’s illustrate this point with some code:
def myFunc(): # function scope a = 20 print(a) # program scope myFunc()
>>> Traceback (most recent call last): >>> File "<untitled>", line 8, in <module> >>> NameError: name 'a' is not defined
a in this case is a local variable: it has been declared inside the function, and it is not available to the global scope outside the function.
Variables defined in the global scope, on the other hand, are available to a function:
def myFunc(): # function scope print(b) # program scope b = 10 myFunc()
But use this with great care. If a function requires one or more values to do its work, it is recommended to pass those values explicitly to the function as arguments. This practice makes code more modular, easier to read and to maintain:
b = 10 def myFunc(b): print(b) myFunc(b)
What is important to understand here is that the global variable
b and the local variable
b are now isolated from each other. If we change
b inside the function, the global variable will remain unchanged:
# global variable b = 10 def my_func(b): b += 10 # local variable print(b) my_func(b)
To avoid confusion, it is recommended to name global and local variables differently:
def quack(ducks): print('quack ' * ducks) n = 5 # amount of ducks quack(n)
>>> quack quack quack quack quack
Write missing documentation for keyword arguments, with examples.
*kwargs– variable function arguments and keyword arguments.
Composition with functions
A function calling another function:
def anotherFunc(x, y): return addNumbers(x, y) # calling the function 'addNumbers' print(anotherFunc(1, 2))
Documentation for functions are a three quoted string after the definition. It’s a very good practice to start from the very first day:
def myFunc(): '''This function prints the string "Hi!".''' print("Hi!")
The ‘pass’ statement
pass statement becomes very handy when writing the structure of our code. It simple does nothing:
def aFunc(x, y): if x > y: print('x is bigger than y') elif x == y: # just a placeholder for code we'll write later pass else: # just a placeholder for code we'll write later pass