Return in Python: Every Type of Return Explained
Return in Python means "we're done". Functions with: Return and one value, Return and several values, Return None, Return void, No Return, and more.
The reserved word “return” in a function declaration is simple, but it hides a few secrets. For starters, you can find declared functions that have one or even several of these types of return:
- Return that returns one value.
- Return that returns several values.
- Return None.
- Empty Return (“return void”).
- No Return.
You may also find these advanced details about “return” interesting (you might already know some of them from using it):
- “return”, in a function declaration, indicates that “we’re done” processing whatever was being done inside the function.
- The declaration of a single function can have one “return”, several “returns” or none.
- A “return” can return one value, several values or none.
- “return”, in the declaration of a “generator function” , indicates that “we’re done” producing elements and raises the StopIteration Exception.
In this article we will reveal each of these secrets, which will give us several tools to get a much broader perspective, make programming easier for us and, therefore, let us write more working code in less time.
Note if you are learning about Python functions: Besides learning about the different types of Return in Python, it is also very interesting to learn about *args and **kwargs, which we explain by clicking here.

You can find the code for this article in Github: View on GitHubInvarato/JarrobaPython / returns
Returning one value
The simplest “return” you can declare in a function is the one that returns a single value:
def dame_un_texto():
return "Texto"
def dame_un_numero():
return 123
def dame_un_booleano():
return true
def dame_un_listado():
return ["A", "B", "C"]
def dame_un_diccionario():
return {
"casa": "house",
"tarta": "cake"
}
Using these should be easy. For example, we could use a couple of them like this:
valor = dame_un_texto()
print(valor)
print(dame_un_listado())
It prints to the screen:
Texto
['A', 'B', 'C']
You can also return None as the only value; we will cover this “return None” later on, because it has some special uses.
def dame_un_none():
return None
Returning several values at once
In Python you can return “several values” at once, and I put it in quotes because what is really returned is a Tuple, and as we will see, this Tuple can be used in a peculiar way in Python.
Returning a Tuple declared with parentheses, with several values inside separated by commas (of the same or different types), is the same as:
def dame_un_tupla():
return ("Texto", True, 123)
Returning these values directly in the “return” without the parentheses (this is what gives the feeling of returning “several values” at once, but really a Tuple is returned).
def dame_varios_valores():
return "Texto", True, 123
We can try printing both:
print(dame_un_tupla())
print(dame_varios_valores())
It will show exactly the same thing, two Tuples:
('Texto', True, 123)
('Texto', True, 123)
Note
Ways to declare a Tuple
A Tuple is normally declared inside parentheses with each value separated by a comma; for example:
mi_tupla = ("Texto", True, 123)
But it can also be declared without parentheses, as long as each value is separated by a comma (commas in a Tuple are mandatory):
mi_tupla = "Texto", True, 123
So a value that ends in a comma will be interpreted as a Tuple with a single element (that is why the comma is mandatory; without the comma it would be interpreted as a single value):
mi_tupla = "Texto",
# Note the comma after the only value
And even if you put parentheses around a one-element Tuple, it still requires the trailing comma:
mi_tupla = ("Texto",)
# Note the comma after the value, inside the parentheses
We can put “the values” we need to return into a variable of type Tuple and return it in the “return” like this (each variable for each value will point to a position of the Tuple):
def dame_varios_valores_en_una_tupla():
# We assume these variables come from a more complicated process
primer_valor = "Texto"
segundo_valor = True
tercer_valor = 123
# Normally this goes right next to the return, but I want to make it clear that this is a way to declare the tuple so that these values can then be used outside the function
mi_tupla = primer_valor, segundo_valor, tercer_valor
return mi_tupla
And it will print the same as before:
print(dame_varios_valores_en_una_tupla())
It shows:
('Texto', True, 123)
This is interesting because if you come from other languages (like Java) where the “return” only returns one element, here it’s “yes and no”. In Python it will return the Tuple:
tupla_devuelta = dame_varios_valores_en_una_tupla()
And we can use its values by their position:
primer_valor = tupla_devuelta[0]
segundo_valor = tupla_devuelta[1]
tercer_valor = tupla_devuelta[2]
But Python makes our life even easier by unpacking these values (like when we declared the Tuple without parentheses, but in reverse):
primer_valor, segundo_valor, tercer_valor = tupla_devuelta
And of course we can do this directly when calling the function and save a lot of code:
primer_valor, segundo_valor, tercer_valor = dame_varios_valores_en_una_tupla()
So we have returned “several values” in the “return” when we have only returned a Tuple and it has automatically been unpacked into several variables.
Return None vs Empty Return (Return void) vs No Return
It’s worth highlighting the difference between “Return None vs Empty Return (Return void) vs No Return”.
The value returned by calling a function whose code goes through any of these three “returns” (“Return None”, “Return void” or “No Return”) is always None:
valor_retornado = funcion_con_uno_de_estos_return()
print(valor_retornado)
For all three functions it simply prints:
None
Return None
It indicates that it is going to return a value to be used outside the function. In this case it is None (but it could have been anything else, like a number or a text). It is usually used to indicate that there is no possible value to return (for example, a function that looks up a piece of data in a database: if the data exists it returns it, and if there is no data to return we can return None to indicate that there was nothing).
In the following example we have a function that translates a word from Spanish to English. If the word is in the dictionary it returns it, and if it isn’t then it returns None:
def traduce_a_ingles(palabra_espanol):
diccionario_espanol_ingles = {
"casa": "house",
"tarta": "cake"
}
try:
return diccionario_espanol_ingles[palabra_espanol]
except KeyError:
return None
You can test it with:
palabra_en_ingles = traduce_a_ingles("casa")
print("casa = {}".format(palabra_en_ingles))
palabra_en_ingles = traduce_a_ingles("habitación")
print("habitación = {}".format(palabra_en_ingles))
It will print to the screen:
casa = house
habitación = None
Empty return (“return void”; it is a “return” with no return values)
It is used to indicate that we’re done (doing whatever is done inside the function) and that the function has no value to return. It is used much like the reserved word “break” in a “for” loop, to end code execution early after doing “something” (calling another function, printing a text with “print”, etc.) when you don’t want to return anything.
The following example searches for a word inside a list with a “for” loop. When it finds it, it prints with “print” that it has found it and ends the function (in this case “return” has a double role: it replaces “break” to end the loop and the function at the same time). I put the “return” on the last line of the function to show that you can include it or not (better not to: it saves you from writing it and the code is cleaner, since there is always supposed to be an “empty return” at the end of the function).
def busca(cosa_a_buscar):
listado_cosas = ["boligrafo", "taza", "cuchara"]
for cosa in listado_cosas:
if cosa == cosa_a_buscar:
print("Encontrado")
return
print("No encontrado")
return # The empty return at the end of the function declaration can be omitted
You can test it with:
busca("taza")
busca("armario")
It will print to the screen:
Encontrado
No encontrado
Warning
Does a “return void” return anything?
As we said at the beginning, this “empty return” returns None, so it makes no sense to assign the function call to a variable, although you could do it without any compile or runtime errors:
valor_retornado = busca("armario")
print(valor_retornado)
But it will always print:
None
This is more of a programming slip: since it does nothing it avoids a runtime error, but it isn’t right to assign something that will never do anything; besides, it can lead people to think that the function does return something, when it will never return anything other than “None”.
In Iterators (more precisely for Generators, which are Iterators) this “empty return” is the only one you are allowed to use (if you add return values, depending on the Python version it will give you an error or the returned value will be useless). The “empty return” in an Iterator indicates that it has finished returning elements (for Generators it indicates that it has finished producing elements). Since this “empty return” has no return value, it also indicates that there is nothing to return (but an Iterator that has finished iterating will never have anything else to return, so it makes sense).
def funcion_generadora_de_numeros_en_texto():
yield "UNO"
yield "DOS"
yield "TRES"
return
iterable = funcion_generadora_de_numeros_en_texto()
Also, the “empty return” raises a StopIteration Exception to signal that the Iterator is exhausted (you don’t have to raise the StopIteration Exception by hand).
You can see it by testing the “generator function” with “next”:
try:
print(next(iterable))
print(next(iterable))
print(next(iterable))
print(next(iterable))
except StopIteration:
print("El Generador se ha agotado y ha devuelto StopIteration")
It prints to the screen:
UNO
DOS
TRES
El Generador se ha agotado y ha devuelto StopIteration
But watch out: even though the “empty return” of a “generator function” raises an Exception, it can never be caught by a try/catch. An example makes it clearer:
def funcion_generadora_de_numeros_en_texto():
yield "UNO"
yield "DOS"
try:
return
except:
print("Nunca se captura nada, ni siquiera el StopIteration que lanza el return")
Warning
StopIteration vs “empty return” in Iterators (Generators, which are Iterators)
It is not a good idea, nor even a good practice, to raise a StopIteration yourself in generator functions (except in very specific cases), since you could hide other Exceptions and it could also be caught by accident where it isn’t needed. For example, inside the same “generator function”:
def funcion_generadora_de_numeros_en_texto():
yield "UNO"
yield "DOS"
try:
raise StopIteration
except:
print("Se imprime esto por la excepción")
It’s important to know that a “generator function” raises a StopIteration, but not that we have to raise it ourselves. It’s better to use the “empty return” to indicate that our Generator is exhausted.
With this we understand that the difference is:
- StopIteration: is an Exception that is raised when a Generator (Generator) has nothing left to produce. It is an Exception, so it can be caught by a try/except.
- Empty return: Indicates that the function has finished and has nothing to return. Also, in an Iterator it raises a StopIteration Exception to signal that this Iterator is exhausted. It is NOT an Exception, therefore, it cannot be caught by a try/except.
No Return
Not putting a return is the optional case of leaving out the “empty return”. That is, this:
def funcion():
# Sin return
Is the same as this:
def funcion():
return
Except that it’s cleaner not to put the “return”, since it is understood that it will return the “empty return”.
And just like before, it will return None, but assigning the function call to a variable is bad style since (just like with the “empty return”) it serves no purpose and clutters the code.
Several returns in the same function
To see the real use of several “returns” in the same function, you need to know that:
- When you put a “return” in a function it indicates that “we’re done” processing whatever was being done inside the function
- That the “return” doesn’t have to be at the end of the function declaration
- There will always be at least the “empty return” at the end of the function, even if we don’t write it
Several “returns” are usually used when there are several values to return and the “if” conditional structure is complex.
For example, a function that paints the color we pass it blue: if we pass “AMARILLO” (yellow) it will paint it “VERDE” (green) and that will be the value it returns. Since we are going to include several colors that mix with blue, we end up with a somewhat complex conditional, so it is easy to put in several “returns”; and in case a value we don’t handle comes into the function, we will return None:
def pinta_de_azul(color):
if color == "BLANCO":
return "AZUL"
elif color == "AMARILLO":
return "VERDE"
elif color == "ROJO":
return "MORADO"
else:
return None
print(pinta_de_azul("AMARILLO"))
print(pinta_de_azul("COLOR QUE NO EXISTE"))
It will print to the screen:
VERDE
None
It is also used to end the function’s code early, for example, inside a loop (like a break, as we saw before, except that here we return a value). And at the end of the function we will always have another “empty return” by default, or we can declare it with a value that suits us.
For example, a function that tells us whether or not a person is at a party. It will look through a “for” loop, person by person in a list, to see if one matches the one we passed in the parameter of the function. If it finds it, it returns True in a return inside the loop, so the loop ends, the function ends and it returns the value; if the loop ends without returning anything, we return False in the return that ends the function:
def esta_en_la_fiesta(persona_a_buscar):
listado_personas_en_la_fiesta = ["Paco", "María", "Juan"]
for persona in listado_personas_en_la_fiesta:
if persona == persona_a_buscar:
return True
return False
print(esta_en_la_fiesta("María"))
print(esta_en_la_fiesta("Rodrigo"))
It will print to the screen:
True
False
References
“Return in Python: Every Type of Return Explained” by Ramón Invarato at jarroba.com, licensed under Creative Commons Attribution-NonCommercial-ShareAlike 3.0. Registered at Safe Creative.

You may also like
Advanced Dockerfile: Multi-Stage Builds, Registries, and Tricks
Free Docker course. Expert Docker tricks: multi-stage builds, optimizing images, faster build times, and distributing images through Docker registries.
Dockerfile: Directives and Dockerizing Projects
Free Docker course. A Dockerfile is a file of directives for building and deploying custom applications with Docker. Learn how to Dockerize your projects.



Leave a comment