Data Structures

There are many types of data structures in Python. Data structures are used for storing and organizing data. In this chapter we will learn about some of the most common data structures in Python such as lists, tuples, dictionaries, and sets.

Create a new Jupyter Notebook called data_structures and follow along.

Lists

You have already learned about lists and have used them a lot. Here we will learn some more things about lists. It’s important to remember that lists are mutable which means you can edit and them and change them after they are created.

Here we will learn about some more common list methods. We have already learned some list methods such as extend and append. Methods are a little different then functions and we will learn more about methods when we learn about object oriented programming. For now, you can sort of think as a method as similar to a function. We will not cover all the list methods but you can check the official documentation for more details

sort

We can use the sort method to sort a list. The sort method does not return any value and sorts the list in place.

x = [10,60,90,100,-5]
print(x)
[10, 60, 90, 100, -5]
x.sort()
print(x)
[-5, 10, 60, 90, 100]

To sort it in reverse you can use the reverse argument.

y = [0,1,2,3,4]
y.sort(reverse=True)
print(y)
[4, 3, 2, 1, 0]
things = ['apple', 'ape', 'dog', 'cat']
print(things)
things.sort()
print(things)
things.sort(reverse=True)
print(things)
['apple', 'ape', 'dog', 'cat']
['ape', 'apple', 'cat', 'dog']
['dog', 'cat', 'apple', 'ape']

count

The count method can count the number of times an item is in a list.

x = [1,2,2,2,3,5]
x.count(-1)
0
x.count(1)
1
x.count(3)
1
x.count(2)
3

pop

The pop method is used to remove an item at a specific index. It removes the item from the list and returns the value of the item that was removed. If you do not specify the position of the item to remove, it will automatically remove the last item in the list.

y = [10, 20, 30, 40, 50]
y.pop(2)
30
print(y)
[10, 20, 40, 50]
y.pop(1)
20
print(y)
[10, 40, 50]
y.pop()
50
print(y)
[10, 40]

remove

The remove method is used to remove the first item in a list that is equal to some value (provided as an argument). If there is no such value, a ValueError is raised.

some_stuff = ['hello', 'bye', 1, 2, -10, 'good', 'bad', 3.14, 'hello']
some_stuff.remove('hello')
print(some_stuff)
['bye', 1, 2, -10, 'good', 'bad', 3.14, 'hello']
some_stuff.remove(3.14)
some_stuff
['bye', 1, 2, -10, 'good', 'bad', 'hello']

List Comprehension

List comprehensions give you a very convenient way to define lists quickly. In this section I will show you several types of examples and you can always Google more examples too. As you read and write Python code, you will find you will be using list comprehensions all the time.

Suppose you wanted to create a list with the integers between 0 and 10. One approach would be this:

numbers = []
for i in range(11):
    numbers.append(i)
print(numbers)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

We can accomplish the same thing in one line using list comprehensions.

numbers = [i for i in range(11)]
print(numbers)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

The general syntax for list comprehensions is

[expression for item in iterable]

Its best to learn these by seeing a bunch of examples.

[x * x for x in range(5)]
[0, 1, 4, 9, 16]
print([character for character in 'Hello there! Hope you have a great day!'])
['H', 'e', 'l', 'l', 'o', ' ', 't', 'h', 'e', 'r', 'e', '!', ' ', 'H', 'o', 'p', 'e', ' ', 'y', 'o', 'u', ' ', 'h', 'a', 'v', 'e', ' ', 'a', ' ', 'g', 'r', 'e', 'a', 't', ' ', 'd', 'a', 'y', '!']
print(numbers)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[x for x in numbers]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[x * x for x in numbers]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[x - x for x in numbers]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[x + x for x in numbers]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
[str(x) for x in numbers]
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

You can even add conditional logic to the list comprehensions.

numbers = [x for x in range(21)]
print(numbers)
even_numbers = [x for x in numbers if x % 2 == 0]
print(even_numbers)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

You can use enumerate in list comprehensions as well:

some_stuff = ['Chris', True, False, 1, 2, 10.8]
[x for x in some_stuff]
['Chris', True, False, 1, 2, 10.8]
[x for i,x in enumerate(some_stuff)]
['Chris', True, False, 1, 2, 10.8]
[i for i,x in enumerate(some_stuff)]
[0, 1, 2, 3, 4, 5]
[[i,x] for i,x in enumerate(some_stuff)]
[[0, 'Chris'], [1, True], [2, False], [3, 1], [4, 2], [5, 10.8]]
countries = ['Canada', 'USA', 'Mexico']
populations = [38000000, 328000000, 126000000]

country_stats = []
for c, p in zip(countries,populations):
    country_stats.append([c,p])
print(country_stats)
[['Canada', 38000000], ['USA', 328000000], ['Mexico', 126000000]]

With list comprehensions you can do the above in one line.

country_stats = [[c,p] for c,p in zip(countries, populations)]
print(country_stats)
[['Canada', 38000000], ['USA', 328000000], ['Mexico', 126000000]]
[x[0] for x in country_stats]
['Canada', 'USA', 'Mexico']
[x[1] for x in country_stats]
[38000000, 328000000, 126000000]
print(country_stats)
[['Canada', 38000000], ['USA', 328000000], ['Mexico', 126000000]]

You can even nest list comprehensions to together. For example we can unpack/flatten the country_stats list like this:

[value for stats in country_stats for value in stats]
['Canada', 38000000, 'USA', 328000000, 'Mexico', 126000000]

If we wanted to do that same thing with for loops we would have to do:

values = []
for stats in country_stats:
    for value in stats:
        values.append(value)
print(values)
['Canada', 38000000, 'USA', 328000000, 'Mexico', 126000000]

There is probably more we could say about list comprehensions but this is a good start. Begin to use them when you code and you will get the hang of it.

Tuples

Now we will learn about tuples. Tuples are sort of like lists but are immutable which means you can not change or edit them after creation. Instead of using [] brackets you use the round brackets () which are also called parenthesis.

x = (1, 2, 3)
x
(1, 2, 3)
type(x)
tuple
len(x)
3
x[0]
1
x[2]
3
x[-1]
3
x
(1, 2, 3)

Seems sort of like a list right? But they are immutable. You can not change them or their values etc.

x[0] = 2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-55-ebac946b3580> in <module>
----> 1 x[0] = 2

TypeError: 'tuple' object does not support item assignment
x.append(10)
x.pop(1)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-56-c18350cddf27> in <module>
----> 1 x.pop(1)

AttributeError: 'tuple' object has no attribute 'pop'

So in that sense, they behave differently then lists.

When you input a tuple you can do so without entering the round brackets.

y = 1,1,1
print(y)
(1, 1, 1)
type(y)
tuple

But it is often a good idea to use the () when entering a tuple because the code is easier to read. Also, sometimes its necessary.

You can nest tuples just like you did with lists.

nested_tuple = ((0, 0), (1, 2), (1, 3), (9, 9))
nested_tuple[-1]
(9, 9)
nested_tuple[-2]
(1, 3)
nested_tuple[-2][0]
1
nested_tuple[-2][1]
3

One thing to watch out for is the syntax when creating a tuple with a single element.

x = (1) # this is not a tuple !
type(x) # it's just an integer
int
print(x)
1

To create a tuple with a single element you must put a trailing comma.

x = (1,)
type(x)
tuple
print(x)
(1,)
x[0]
1
x[1] # only has one element so this index is out of range
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-72-a81caa627b71> in <module>
----> 1 x[1] # only has one element so this index is out of range

IndexError: tuple index out of range

Tuple packing is when you pack things together in a tuple. For example:

t = ('hello world', 1, True, False, 'Bye')
print(t)
('hello world', 1, True, False, 'Bye')
type(t)
tuple
len(t)
5

You can unpack the tuple t like this for example:

print(t)
a, b, c, d, e = t
('hello world', 1, True, False, 'Bye')
a
'hello world'
b
1
c
True
d
False
e
'Bye'

Tuples are used with enumerate.

print(some_stuff)
['Chris', True, False, 1, 2, 10.8]
[type(x) for x in enumerate(some_stuff)]
[tuple, tuple, tuple, tuple, tuple, tuple]
[x for x in enumerate(some_stuff)]
[(0, 'Chris'), (1, True), (2, False), (3, 1), (4, 2), (5, 10.8)]

To grab just the second element (or first) in each tuple you could do:

[b for a,b in enumerate(some_stuff)]
['Chris', True, False, 1, 2, 10.8]
[a for a,b in enumerate(some_stuff)]
[0, 1, 2, 3, 4, 5]

Here is a list of tuples.

data = [(0,-1,2), (0,4,8), (-2,-2,5), (4,5,6)]
for d in data:
    print(d)
(0, -1, 2)
(0, 4, 8)
(-2, -2, 5)
(4, 5, 6)

Or we can unpack each tuple in the loop like this:

for a,b,c in data:
    print(f'{a} + {b} + {c} is {a + b + c}')
0 + -1 + 2 is 1
0 + 4 + 8 is 12
-2 + -2 + 5 is 1
4 + 5 + 6 is 15

Because data is a list we can change it.

data.append('Chris')
print(data)
[(0, -1, 2), (0, 4, 8), (-2, -2, 5), (4, 5, 6), 'Chris']
data.pop(0)
(0, -1, 2)
data
[(0, 4, 8), (-2, -2, 5), (4, 5, 6), 'Chris']
data[2] = False
data
[(0, 4, 8), (-2, -2, 5), False, 'Chris']

But we still can not change one of the tuple items directly.

data[0]
(0, 4, 8)
data[0][-1] = 9
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-98-2b4bf7263d17> in <module>
----> 1 data[0][-1] = 9

TypeError: 'tuple' object does not support item assignment

We will work with tuples more later.

Dictionaries

Dictionaries are another type of data structure in Python and they are super useful. They are mutable so you can edit them and change them after they are created. Think of them as a “look up” where you look up a key and get a value. Dictionaries are essentially a mapping between keys and values. You can define a dictionary by enclosing a comma separated list of key-value pairs in curly braces {}. You use a colon : to separate each key from its associated value.

Here is an example of a dictionary with a persons name as the key and their age as the value.

ages = {'Chris': 35, 'Joanna': 37, 'Penny': 11}
print(ages)
{'Chris': 35, 'Joanna': 37, 'Penny': 11}
type(ages)
dict
len(ages)
3

keys()

To list the keys in a dictionary you can use the keys() method.

ages.keys()
dict_keys(['Chris', 'Joanna', 'Penny'])
list(ages.keys())
['Chris', 'Joanna', 'Penny']

values()

To get the values you can use the values() method.

ages.values()
dict_values([35, 37, 11])
list(ages.values())
[35, 37, 11]

If you wrap a dictionary within list you will only get the keys.

list(ages)
['Chris', 'Joanna', 'Penny']

Extracting and Storing Values

So the dictionary is a mapping between the key and the value.

KEY ——–> VALUE

'Chris' ——–> 35

'Joanna' ——–> 37

'Penny' ——–> 11

The two main operations your perform on a dictionary are

  1. extracting the value given the key

  2. storing a value with some key

To extract the value of a key you do this:

ages['Penny']
11

It is sort of like getting the value at a position of a list. But instead of indexing the position, which is an integer, you use the key as the index.

ages['Chris']
35
ages['Joanna']
37

If you try to get the value of a key that is not in the dictionary you will get an error. In particular, a KeyError.

ages['Hazel']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-111-bde46f9036a6> in <module>
----> 1 ages['Hazel']

KeyError: 'Hazel'

Dictionaries are mutable so we can keep adding additional key value pairs. This is how you store a value with some key.

ages['Hazel'] = 7
print(ages)
{'Chris': 35, 'Joanna': 37, 'Penny': 11, 'Hazel': 7}
ages['Isaac'] = 9
print(ages)
{'Chris': 35, 'Joanna': 37, 'Penny': 11, 'Hazel': 7, 'Isaac': 9}
print(list(ages.keys()))
['Chris', 'Joanna', 'Penny', 'Hazel', 'Isaac']
print(list(ages.values()))
[35, 37, 11, 7, 9]
ages['Isaac']
9
ages['Hazel']
7

in

You can use the in membership operator to see if a key is in a dictionary or if a value is in the dictionary. More common to check if a key is in a dictionary though.

names = ['Larry', 'Joanna', 'Karen', 'Hazel', 'Isaac', 'Penny', 'Chris', 'Matt', 'Jen']
len(names)
9
ages
{'Chris': 35, 'Joanna': 37, 'Penny': 11, 'Hazel': 7, 'Isaac': 9}
'Chris' in ages # is checking if 'Chris' is one of the keys
True
'Matt' in ages
False
35 in ages
False
35 in ages.values()
True
'Chris' in ages.keys()
True
9 in ages # same as 9 in ages.keys()
False
9 in ages.keys()
False
9 in ages.values()
True
names
['Larry', 'Joanna', 'Karen', 'Hazel', 'Isaac', 'Penny', 'Chris', 'Matt', 'Jen']
ages
{'Chris': 35, 'Joanna': 37, 'Penny': 11, 'Hazel': 7, 'Isaac': 9}
for name in names:
    if name in ages:
        print(f'{name} is in the dictionary ages and their age is {ages[name]}.')
    else:
        print(f'{name} is not in the dictionary ages so we do not know their age.')
Larry is not in the dictionary ages so we do not know their age.
Joanna is in the dictionary ages and their age is 37.
Karen is not in the dictionary ages so we do not know their age.
Hazel is in the dictionary ages and their age is 7.
Isaac is in the dictionary ages and their age is 9.
Penny is in the dictionary ages and their age is 11.
Chris is in the dictionary ages and their age is 35.
Matt is not in the dictionary ages so we do not know their age.
Jen is not in the dictionary ages so we do not know their age.

Different ways to create

There are multiple ways to create/build dictionaries. We saw one above where we we just explicitly defined the initial dictionary within the curly braces {}.

You can use dict to build a dictionary from a list of key-value pairs.

list_of_ages = [('Joanna', 37), ('Chris', 35), ('Penny', 11)] # list of tuples
dict(list_of_ages)
{'Joanna': 37, 'Chris': 35, 'Penny': 11}

If the keys are simple strings then you can even use keyword arguments like this.

dict(Joanna=1, Chris=35, Penny=11) # only works when the keys will be strings
{'Joanna': 1, 'Chris': 35, 'Penny': 11}

You can use dict comprehension to build dictionaries too. Suppose we wanted to create a dictionary with the keys as integers and the values as those integers squared (the number times it self).

numbers_squared = {
    0: 0 * 0, 
    1: 1 * 1, 
    2: 2 * 2, 
    3: 3 * 3,
    4: 4 * 4,
    5: 5 * 5,
    6: 6 * 6,
    7: 7 * 7,
    8: 8 * 8,
    9: 9 * 9
}
numbers_squared
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
numbers_squared[6]
36

We can use a dict comprehension to generate the above dictionary.

{x: x * x for x in range(10)}
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Looping techniques

Now we will look at some looping techniques when working with dictionaries.

ages
{'Chris': 35, 'Joanna': 37, 'Penny': 11, 'Hazel': 7, 'Isaac': 9}
numbers_squared
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
# this will only give the keys
for k in ages:
    print(k)
Chris
Joanna
Penny
Hazel
Isaac
# this will only give the keys
for k in numbers_squared:
    print(k)
0
1
2
3
4
5
6
7
8
9
for k in ages:
    print(k, ages[k])
Chris 35
Joanna 37
Penny 11
Hazel 7
Isaac 9
for k in numbers_squared:
    print(k, numbers_squared[k])
0 0
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81

You can use items() method to iterate over a dictionary and get the key and value at the same time.

for k,v in ages.items():
    print(k,v)
Chris 35
Joanna 37
Penny 11
Hazel 7
Isaac 9
for k,v in numbers_squared.items():
    print(k,v)
0 0
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81

You can create an empty dictionary like this

ages = {}
print(ages)
{}
names_list = ['Larry', 'Joanna', 'Karen', 'Hazel', 'Isaac', 'Penny', 'Chris', 'Matt', 'Jen']
ages_list = [60, 37, 58, 7, 9, 11, 35, 38, 40]
for name,age in zip(names_list,ages_list):
    print(name,age)
Larry 60
Joanna 37
Karen 58
Hazel 7
Isaac 9
Penny 11
Chris 35
Matt 38
Jen 40

We can add these names and ages to the empty dict ages, and populate it within a loop.

ages = {}
for name,age in zip(names_list,ages_list):
    ages[name] = age
ages
{'Larry': 60,
 'Joanna': 37,
 'Karen': 58,
 'Hazel': 7,
 'Isaac': 9,
 'Penny': 11,
 'Chris': 35,
 'Matt': 38,
 'Jen': 40}

Keys are unique

list(ages.keys())
['Larry', 'Joanna', 'Karen', 'Hazel', 'Isaac', 'Penny', 'Chris', 'Matt', 'Jen']
list(ages.values())
[60, 37, 58, 7, 9, 11, 35, 38, 40]
ages['Larry']
60
ages['Chris']
35
ages['Henry'] # not a key in the dict
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-159-e2b0d9f786ff> in <module>
----> 1 ages['Henry'] # not a key in the dict

KeyError: 'Henry'
# add the key and a value
ages['Henry'] = 'eleven years old'
ages
{'Larry': 60,
 'Joanna': 37,
 'Karen': 58,
 'Hazel': 7,
 'Isaac': 9,
 'Penny': 11,
 'Chris': 35,
 'Matt': 38,
 'Jen': 40,
 'Henry': 'eleven years old'}

There can not be duplicate keys in a dictionary. The keys must be unique. So if you assign a value to a key that is already in the dictionary it will be updated.

ages['Henry']
'eleven years old'
ages['Henry'] = 11
ages
{'Larry': 60,
 'Joanna': 37,
 'Karen': 58,
 'Hazel': 7,
 'Isaac': 9,
 'Penny': 11,
 'Chris': 35,
 'Matt': 38,
 'Jen': 40,
 'Henry': 11}
for k, v in ages.items():
    print(f'{k} is {v} years old.')
    
Larry is 60 years old.
Joanna is 37 years old.
Karen is 58 years old.
Hazel is 7 years old.
Isaac is 9 years old.
Penny is 11 years old.
Chris is 35 years old.
Matt is 38 years old.
Jen is 40 years old.
Henry is 11 years old.

Sets

Python sets are quite simple but can be very useful in certain situations. A set is an unordered collection with no duplicate elements.

fruits_list = [ 'oranges', 'grapefruits', 'mandarins', 'limes', 'limes', 'apple', 'apple']
print(fruits_list)
['oranges', 'grapefruits', 'mandarins', 'limes', 'limes', 'apple', 'apple']

With a regular list, like the list of fruits_list above, you can have duplicates. You can use set to get the unique items.

fruits_set = set(fruits_list)
print(fruits_set)
type(fruits_set)
{'limes', 'apple', 'oranges', 'grapefruits', 'mandarins'}
set

You see that a set is a list of item between curly braces {}.

my_set_of_stuff = {'Hello', 10, 20, True, False, 3.1459, 'a', False, True, 20}
my_set_of_stuff # see how the duplicates were removed
{10, 20, 3.1459, False, 'Hello', True, 'a'}

You can create an empty set like this:

x = set()
type(x)
set

Don’t use {} to create any empty set because it will actually create an empty dictionary.

y = {}
type(y)
dict

add()

You can use the add() method to add items to a set.

x = set() # empty set
x.add('HELLO')
x
{'HELLO'}
x.add(True)
x
{'HELLO', True}
x.add(True) # is already in the set
x
{'HELLO', True}
for i in range(11):
    if 3 < i < 7:
        x.add(i)
        
len(x)
5
x
{4, 5, 6, 'HELLO', True}
5 in x
True
4 in x
True
7 in x
False

remove

print(x)
{True, 4, 5, 6, 'HELLO'}
x.remove(8) # not in the set so raises error
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-187-ed5a54533654> in <module>
----> 1 x.remove(8) # not in the set so raises error

KeyError: 8
x.remove('HELLO')
x
{True, 4, 5, 6}
x.remove(5)
x
{True, 4, 6}

Like many of the different data structures introduced here, there is more that could be said. Google is your friend when it comes to learning Python. It’s not possible to cover every single topic here. The objective here is to introduce you to the main concepts so you can use these tools when building programs. Then when you get stuck or need some extra functionality, you can do some searching with Google about more information related to these data structures.

We will end this chapter by returning to functions and learning about *args and **kwargs. I left this topic until now because it requires some knowledge of tuples and dictionaries.

Function *args and **kwargs

In this section we show how you can have a variable number of positional arguments or keyword arguments with the use of *args and **kwargs. Sometimes you don’t know exactly the future use cases of a function or you may need to have support for additional arguments without defining them up front. These are examples where you can use a variable number of arguments which means you do not specify the exact number of arguments. Let’s look at some examples.

*args

*args is used for positional arguments.

def add_numbers(a, b):
    return a + b
add_numbers(1,2)
3

The above function takes two arguments and adds them together. But what if we wanted to have any number of arguments and add them all together? You can do that with the unpacking operator *. First we will use *args which supports any number of positional arguments. The function will not add the arguments yet. First we will see that by using*args, you can pass any number of positional arguments and args is a tuple in the function definition.

def add_numbers(*args):
    print(args)
    print(type(args))
add_numbers()
()
<class 'tuple'>
add_numbers(1)
(1,)
<class 'tuple'>
add_numbers(1,2)
(1, 2)
<class 'tuple'>
add_numbers(1,2,4)
(1, 2, 4)
<class 'tuple'>
add_numbers(1,2,4,-9)
(1, 2, 4, -9)
<class 'tuple'>

You see? We can pass any number of positional arguments to the function and they are all available within a variable name called args which is a tuple. So to actually add the numbers we can just sum the elements in the tuple.

def add_numbers(*args):
    return sum(args)
add_numbers()
0
add_numbers(1,2,3)
6
add_numbers(10,20,30,40,50,60)
210

Here is another example.

def do_stuff_with_args(*args):
    print(f'You passed {len(args)} positional arguments.')
    print(f'These positional arguments are available in the tuple {args}.')
    for i,x in enumerate(args):
        print(f'The argument at position {i} is {x}.')
    print('We will return the last argument:')
    return args[-1]
do_stuff_with_args(1,2,3)
You passed 3 positional arguments.
These positional arguments are available in the tuple (1, 2, 3).
The argument at position 0 is 1.
The argument at position 1 is 2.
The argument at position 2 is 3.
We will return the last argument:
3
do_stuff_with_args('HI', 'BYE', True, False, 3.14159)
You passed 5 positional arguments.
These positional arguments are available in the tuple ('HI', 'BYE', True, False, 3.14159).
The argument at position 0 is HI.
The argument at position 1 is BYE.
The argument at position 2 is True.
The argument at position 3 is False.
The argument at position 4 is 3.14159.
We will return the last argument:
3.14159

And it’s typical to use the name args but you can actually use any name at all provided that you use the unpacking * operator. For example:

def do_stuff_with_args(*stuff):
    print(f'You passed {len(stuff)} arguments.')
    print(f'There are available in the tuple {stuff}')
    for i,x in enumerate(stuff):
        print(f'The argument at position {i} is {x}')
    print('We will return the last argument:')
    return stuff[-1]
do_stuff_with_args(3+4,6+4)
You passed 2 arguments.
There are available in the tuple (7, 10)
The argument at position 0 is 7
The argument at position 1 is 10
We will return the last argument:
10

**kwargs

**kwargs is similar to *args but instead is used for a variable number of keyword arguments. Instead of a tuple, kwargs is a dictionary.

def get_person_details(**kwargs):
    print(kwargs)
    print(type(kwargs))
get_person_details(name='Chris', age=36, city='Halifax')
{'name': 'Chris', 'age': 36, 'city': 'Halifax'}
<class 'dict'>
def get_person_details(**kwargs):
    for k,v in kwargs.items():
        print(f'The argument {k} has the value {v}.')
        
get_person_details(name='Chris', age=36, city='Halifax')
The argument name has the value Chris.
The argument age has the value 36.
The argument city has the value Halifax.

So that’s how you can pass a variable number (i.e. any number) of keyword arguments using **kwargs. And you can use any other name as long as you use the **. For example

def get_person_details(**details):
    for k,v in details.items():
        print(f'The argument {k} has the value {v}.')
        
get_person_details(name='Chris', age=36, city='Halifax', country='Canada')
The argument name has the value Chris.
The argument age has the value 36.
The argument city has the value Halifax.
The argument country has the value Canada.

It’s just common to see *args and **kwargs but you can use any names for these that you want.

  • * is for positional arguments and are accessed with a tuple

  • ** is for keyword arguments and are accessed with a dictionary

Ordering of arguments

We saw in an earlier section that positional arguments must come before keyword arguments. So that means *args must come before **kwargs.

def get_person_details(*args, **kwargs):
    print(args)
    print(kwargs)
get_person_details('Chris', 36, city='Halifax', country='Canada')
('Chris', 36)
{'city': 'Halifax', 'country': 'Canada'}

Just remember that positional arguments come before keyword arguments. In general the ordering for arguments is:

  • regular positional arguments

  • *args

  • regular keyword arguments

  • **kwargs

Like in this example:

def get_person_details(name, age, *args, country='Canada', **kwargs):
    print(f'Name is {name}.')
    print(f'Age is {age}.')
    print(f'Country is {country}.')
    
    print(f'Other positional arguments given were: {args}.')
    
    print(f'Other keyword arguments given were:')
    for k,v in kwargs.items():
        print(f'{k}: {v}')
get_person_details('Chris',
                   35,
                   'Short hair',
                   '6 feet tall', 
                   hair_color='brown', 
                   eye_color='blue',
                   sport='basketball',
                   movie='LOTR')
Name is Chris.
Age is 35.
Country is Canada.
Other positional arguments given were: ('Short hair', '6 feet tall').
Other keyword arguments given were:
hair_color: brown
eye_color: blue
sport: basketball
movie: LOTR

Okay so that’s an introduction to *args and **kwargs. I’m sure we will use them later throughout the course. It’s good to be aware of them.