Sets, Tuples, and Dictionaries
Site: | Saylor Academy |
Course: | CS250: Python for Data Science |
Book: | Sets, Tuples, and Dictionaries |
Printed by: | Guest user |
Date: | Saturday, September 7, 2024, 8:12 PM |
Description
Here is more practice with tuples and dictionaries. In addition, the Python built-in data structure known as a set is also covered. Sets are not ordered, and their elements cannot be indexed (sets are not lists). To understand Python set operations, remind yourself of basic operations such as the union and intersection. Use this tutorial to compare and contrast the syntax and programming uses for lists, tuples, sets, and dictionaries.
Introduction
Now that we've got the basics of strings and numbers and lists down, let's talk about the advanced data types
- tuple
, dict
and set
. These are container objects that let us organize other types of objects into one data structure.
Source: Nina Zakharenko, https://practical.learnpython.dev/03_sets_tuples_dicts/ This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 License.
Tuples
Tuples are light-weight collections used to keep track of related, but different items. Tuples are immutable, meaning that once a tuple has been created, the items in it can't change. You can't add, remove, or update items like you can with a list.
tuple cheat sheet
type | tuple |
---|---|
use | Used for storing a snapshot of related items when we don't plan on modifying, adding, or removing data. |
creation | () or tuple() for empty tuple. (1, ) for one item, or (1, 2, 3) for a tuple with several items. |
search methods | my_tuple.index(item) or item in my_tuple |
search speed | Searching for an item in a large tuple is slow. Each item must be checked. |
common methods | Can't add or remove from tuples. |
order preserved? | Yes. Items can be accessed by index. |
mutable? | No |
in-place sortable? | No |
Uses
You might ask, whytuple
when Python already has lists? tuple
is different in a few ways. While lists are generally used to store collections of similar items together, tuple
s, by contrast, can be used to contain a snapshot of data. A good use of a tuple
might be for storing the information for a row in a spreadsheet. We don't necessarily care about updating or manipulating that data, we just want a read-only snapshot.tuple
is an interesting and powerful datatype, and one of the more unique aspects of Python. Most other programming languages have ways of representing lists and dictionaries, but only a small subset contain tuples. Use them to your advantage.Examples
Empty and one-item tuples
One important thing to note about tuples is that there's a quirk to their creation. Let's check the type of an empty tuple created with ().
>>> a = ()
>>> type(a)
<class 'tuple'>
That looks like we'd expect it to. What about if we tried to create a one-item tuple using the same syntax?
>>> b = (1)
>>> type(b)
<class 'int'>
It didn't work! type((1)) is an int. In order to create a one-item tuple, you'll need to include a trailing comma inside the parenthesis.
>>> c = (1, )
>>> type(c)
<class 'tuple'>
Tip
Creation
Let's say we have a spreadsheet of students, and we'd like to represent each row as a tuple.>>> student = ("Marcy", 8, "History", 3.5)
Access by index
We can access items in the tuple by index.>>> student = ("Marcy", 8, "History", 3.5)
>>> student[0]
'Marcy'
But if we try to change the contents, we'll get an error. Remember, you can't change the items in a tuple.
>>> student[0] = "Bob"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Info
TypeError: 'tuple' object does not support item assignment
if we try to change the items in a tuple.append
or extend
method available on them like lists do, because they can't be changed.tuple unpacking.
Sounds like a lot of work for not a lot of benefit, right? Not so.tuple
is great when you depend on your data staying unchanged. Because of this guarantee, we can use tuples in other types of containers like set
s and dict
ionaries that expect immutable keys. We'll learn more about this later in the chapter.It's also a great way to quickly consolidate information.
You can also use
tuple
s for something called unpacking. Let's see it in action:>>> student = ("Marcy", 8, "History", 3.5)
>>>
>>> name, age, subject, grade = student
>>> name
'Marcy'
>>> age
8
>>> subject
'History'
>>> grade
3.5
You can return tuples from functions, and use unpacking to get the values back.
>>> def http_status_code():
... return 200, "OK"
...
>>> code, value = http_status_code()
>>> code
200
>>> value
'OK'
Sets
Aset
is a mutable datatype that allows you to store immutable types in an unsorted way. Sets are mutable because you can add and remove items from them. They can contain immmutable items, like tuple
s and other primitive types, but not list
s, set
s, or dict
ionaries which are themselves mutable.Unlike a list or a tuple, a set can only contain one instance of a unique item. There are no duplicates allowed.
union
and intersection
.set cheat sheet
type | set |
---|---|
use | Used for storing immutable data types uniquely. Easy to compare the items in set s. |
creation | set() for an empty set ({} makes an empty dict ) and {1, 2, 3} for a set with items in it |
search methods | item in my_set |
search speed | Searching for an item in a large set is very fast. |
common methods | my_set.add(item) , my_set.discard(item) to remove the item if it's present, my_set.update(other_set) |
order preserved? | No. Items can't be accessed by index. |
mutable? | Yes. Can add to or remove from set s. |
in-place sortable? | No, because items aren't ordered. |
Examples
Empty sets
Let's create our first few sets.The first thing we might try to do is create an empty set with
{}
, but we'll come across a hurdle.>>> my_new_set = {}
>>> type(my_new_set)
<class 'dict'>
>>> my_set = set()
>>> type(my_set)
<class 'set'>
Info
{}
. That creates a dict
. Create an empty set with set()
instead.Tip
type()
, dir()
and help()
as often as possible.sets with items
sets can't contain duplicate values
>>> numbers = {3, 3, 2, 2, 1}
>>> numbers
{1, 2, 3}
sets can be used to de-duplicate the items in a list
Tip: Use this to your advantage when you need to quickly deduplicate the items in alist
if you don't care about order. Pass the list
into the set
constructor.>>> names = ['Nina', 'Max', 'Nina']
>>> set(names)
{'Nina', 'Max'}
sets can't contain mutable types
You can consider a set to behave a lot like a dictionary that only contains keys (and no values). The way thatset
s allow you to quickly check if an item is contained in them or not is with an algorithm called a hash. I won't cover the details, but an algorithm is a way of representing an immutable data type with a unique numerical representation. An immutable data type is one where the contents can't be changed after creation.If you're not sure if a type or an object is hashable, you can call the built-in
hash()
function on it.>>> hash("Nina")
3509074130763756174
>>> hash([1, 2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Info
TypeError: unhashable type: 'list'
if you try to add a mutable data type (like a list
) to a set.If you try to add a mutable data type (like a
list
) to a set, you'll see the same TypeError
, complaining about an unhashable type
.>>> {"Nina"}
{'Nina'}
>>> {[]}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
sets don't have an order
Sets don't have an order. That means that when you print them, the items won't be displayed in the order they were entered in the list.>>> my_set = {1, "a", 2, "b", "cat"}
>>> my_set
{1, 2, 'cat', 'a', 'b'}
It also means that you can't access items in the
set
by position in subscript []
notation.>>> my_set = {"Red", "Green", "Blue"}
>>> my_set[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'set' object does not support indexing
Info
TypeError: 'set' object does not support indexing
if you try to access the items in a set
by index with my_set[pos]
Tip: If your set contains items of the same type, and you want to sort the items, you'll need to convert the
set
to a list
first.Or, you can use the built-in
sorted(sequence)
method, which will do the conversion for you.>>> my_set = {"a", "b", "cat", "dog", "red"}
>>> my_set
{'b', 'red', 'a', 'cat', 'dog'}
>>> sorted(my_set)
['a', 'b', 'cat', 'dog', 'red']
adding to and removing from sets
Since a set has no order, we can't add or remove items to it by index.Instead, the operations are called with items.
Add items to a set with
my_set.add(item)
.>>> colors = {"Red", "Green", "Blue"}
>>> colors.add("Orange")
>>> colors
{'Orange', 'Green', 'Blue', 'Red'}
Remove items with my_set.discard(item)
You can remove an item from a set
if it's present with my_set.discard(item)
. If the set doesn't contain the item, no error occurs.>>> colors = {"Red", "Green", "Blue"}
>>> colors.discard("Green")
>>> colors
{'Blue', 'Red'}
>>> colors.discard("Green")
>>> colors
{'Blue', 'Red'}
set
with my_set.remove(item)
, which will raise a KeyError
if the item doesn't exist.Update a set with another sequence using my_set.update(sequence)
You can add to the items in a set
by passing in another sequence such as a set
, list
, or tuple
to the update()
method.>>> colors = {"Red", "Green"}
>>> numbers = {1, 3, 5}
>>> colors.update(numbers)
>>> colors
{1, 3, 'Red', 5, 'Green'}
Info
my_set.update(sequence)
. That's because a str
ing is a sequence of characters under the hood.>>> numbers = {1, 3, 5}
>>> numbers.update("hello")
>>> numbers
{1, 3, 'h', 5, 'o', 'e', 'l'}
str
ing, which was probably not your intended result.set
operations
set
s allow quick and easy operations to compare items between two set
s.set operations cheat sheet
type | set |
---|---|
use | Used for storing immutable data types uniquely. Easy to compare the items in set s. |
creation | set() for an empty set ({} makes an empty dict ) and {1, 2, 3} for a set with items in it |
search methods | item in my_set |
search speed | Searching for an item in a large set is very fast. |
common methods | my_set.add(item) , my_set.discard(item) to remove the item if it's present, my_set.update(other_set) |
order preserved? | No. Items can't be accessed by index. |
mutable? | Yes. Can add to or remove from set s. |
in-place sortable? | No, because items aren't ordered. |
Examples
Let's see it in action.We have two sets,
rainbow_colors
, which contain the colors of the rainbow, and favorite_colors
, which contain my favorite colors.>>> rainbow_colors = {"Red", "Orange", "Yellow", "Green", "Blue", "Violet"}
>>> favorite_colors = {"Blue", "Pink", "Black"}
First, let's combine the sets and create a new
set
that contains all of the items from rainbow_colors
and favorite_colors
using the union operation. You can use the my_set.union(other_set)
method, or you can just use the symbol for union |
from the table above.>>> rainbow_colors | favorite_colors
{'Orange', 'Red', 'Yellow', 'Green', 'Violet', 'Blue', 'Black', 'Pink'}
set
with only the items in both set
s.>>> rainbow_colors & favorite_colorsThere are other useful operations available on
{'Blue'}
set
s, such as checking if one set is a subset, a superset, differences, and more, but I don't have time to cover them all. Python also has a frozenset
type, if you need the functionality of a set
in an immutable package (meaning that the contents can't be changed after creation).Dictionaries
Dictionaries are a useful type that allow us to store our data in key, value pairs. Dictionaries themselves are mutable, but, just like sets, dictionary keys can only be immutable types.
We use dictionaries when we want to be able to quickly access data associated with a key.
A great practical application for dictionaries is memoization. Let's say you want to save computing power, and store the result for a function called with particular arguments. The arguments could be the key, with the result stored as the value. Next time someone calls your function, you can check your dictionary to see if the answer is pre-computed.
Looking for a key in a large dictionary is extremely fast. Unlike lists, we don't have to check every item for a match.
dictionary cheat sheet
type | dict |
---|---|
use | Use for storing data in key, value pairs. Keys used must be immutable data types. |
creation | {} or dict() for an empty dict . {1: "one", 2: "two"} for a dict with items. |
search methods | key in my_dict |
search speed | Searching for a key in a large dictionary is fast. |
common methods | my_dict[key] to get the value by key , and throw a KeyError if key is not in the dictionary. Use my_dict.get(key) to fail silently if key is not in my_dict . my_dict.items() for all key, value pairs, my_dict.keys() for all keys, and my_dict.values() for all values. |
order preserved? | Sort of. As of Python 3.6 a dict is sorted by insertion order. Items can’t be accessed by index, only by key. |
mutable? | Yes. You can add or remove keys from dict s. |
in-place sortable? | No. dict s don’t have an index, only keys. |
Examples
Empty dict
s
We already learned one of the methods of creating an empty dict
when we tried (and failed) to create an empty set with {}
. The other way is to use the dict()
method.>>> my_dict = {}
>>> type(my_dict)
<class 'dict'>
>>> my_dict = dict()
>>> type(my_dict)
<class 'dict'>
Creating dict
s with items
If we want to create dict
s with items in them, we need to pass in key and value pairs. A dict
is declared with curly braces {}
, followed by a key and a value, separated with a colon :
. Multiple key and value pairs are separated with commas ,
.We can call familiar methods on our dictionary, like finding out how many key / value pairs it contains with the built-in
len(my_dict)
method.>>> nums = {1: "one", 2: "two", 3: "three"}
>>> len(nums)
3
Side note: What can be used as keys?
Any type of object, mutable or immutable, can be used as a value but just likeset
s, dict
ionaries can only use immutable types as keys. That means you can use int
, str
, or even tuple
as a key, but not a set
, list
, or other dict
ionary.The following is OK:
>>> my_dict = {1: 1}
>>> my_dict = {1: []}
Info
TypeError: unhashable type: 'list'
if you try to use a mutable type, like a list
, as a dict
ionary key.>>> my_dict = {[]: 1}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Accessing
Ourdict
contains key
, value
pairs. Because a dict
ionary isn't ordered, we can't access the items in it by position. Instead, to access the items in it, we use square-bracket my_dict[key]
notation, similar to how we access items in a list.>>> nums = {"one": 1, "two": 2, "three": 3}
>>> nums["one"]
1
>>> nums["two"]
2
Question: What happens when we try to access a key that doesn't exist in a
dict
?Info
KeyError: key
if we try to access my_dict[key]
but key
isn't in the dictionary.>>> nums = {1: "one", 2: "two", 3: "three"}One way to get around this is to use the
>>> nums[4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 4
my_dict.get(key)
method. Using this method, if the key isn't present, no error is thrown, and no value (aka the None
type) is returned.>>> nums = {1: "one", 2: "two", 3: "three"}If we want to provide a default value if the key is missing, we also pass an optional argument to the
>>> nums.get(4)
>>> result = nums.get(4)
>>> type(result)
<class 'NoneType'>
my_dict.get(key)
method like so: my_dict.get(key, default_val)
>>> nums = {1: "one", 2: "two", 3: "three"}
>>> nums.get(4, "default")
'default'
Adding and Removing Items
To add a new key value pair to the dictionary, you'll use square-bracket notation.If you try to put a key into a dictionary that's already there, you'll just end up replacing it.
Tip: To avoid subtle bugs, you can check if a particular key is in a dictionary with the
in
keyword.
>>> nums = {1: "one", 2: "two", 3: "three"}
>>> nums[8] = "eight"
>>> nums
{1: 'one', 2: 'two', 3: 'three', 8: 'eight'}
>>> 8 in nums
True
>>> nums[8] = "oops, overwritten"
>>> nums
{1: 'one', 2: 'two', 3: 'three', 8: 'oops, overwritten'}
Updating
Just like withlist
s an set
s, you can add the contents of one dictionary to another with the update()
method.>>> colors = {"r": "Red", "g": "Green"}
>>> numbers = {1: "one", 2: "two"}
>>> colors.update(numbers)
>>> colors
{'r': 'Red', 'g': 'Green', 1: 'one', 2: 'two'}
Complex Dictionaries
One incredibly useful scenario for dictionaries is storing the values in alist
or other sequence.>>> colors = {"Green": ["Spinach"]}
>>> colors
{'Green': ['Spinach']}
>>> colors["Green"].append("Apples")
>>> colors
{'Green': ['Spinach', 'Apples']}
Working with items
, keys
, and values
There are three useful methods you need to remember about dict
ionary access:my_dict.keys()
my_dict.values()
my_dict.items()
1. my_dict.keys()
gets all the keys in a dictionary
>>> nums = {1: 'one', 2: 'two', 3: 'three', 8: 'eight'}
>>> nums.keys()
dict_keys([1, 2, 3, 8])
2. my_dict.values()
gets all the values in a dictionary.
>>> nums = {1: 'one', 2: 'two', 3: 'three', 8: 'eight'}
>>> nums.values()
dict_values(['one', 'two', 'three', 'eight'])
3. my_dict.items()
gets all the items (key, value pairs) in a dictionary
Notice that my_dict.items()
returns a type that looks like a list.Note: this data structure looks like it contains two-item
tuple
s containing the key, value pairs. Remember this, it'll be important later on!'python >>> nums = {1: 'one', 2: 'two', 3: 'three', 8: 'eight'} >>> nums.items() dict_items([(1, 'one'), (2, 'two'), (3, 'three'), (8, 'eight')])
Mutability
Mutability, simply put: the contents of a mutable object can be changed, while the contents of an immutable object cannot.
Simple Types
All of the simple data types we covered first are immutable.
You can replace the value, but you can't change it.
type | use | mutable? |
---|---|---|
int , float , decimal |
store numbers | no |
str |
store strings | no |
bool |
store True or False |
no |
Container Types
container type | use | mutable? |
---|---|---|
list |
ordered group of items, accessible by position | yes |
set |
mutable unordered group consisting only of immutable items. useful for set operations (membership, intersection, difference, etc) | yes |
tuple |
immutable collection containing ordered groups of items | no |
dict |
contains key value pairs | yes |
Practice
Sets
Sets are a great data type for storing unique data - you can only have one of any given object in a set. Sets are unordered, thus you can't access them with [] indexing syntax, but they do have some handy functions.
Let's play with some set operations:
# Create an empty set
>>> my_set = {}
>>> type(my_set)
# Gotcha: using {} actually creates an empty dictionary. To create an empty set, use set()
>>> my_set = set()
>>> type(my_set)
# Let's create a non-empty set
>>> my_set = {1, 2, 3}
# We can add and remove items from the set
>>> my_set.add(4)
>>> my_set.remove(2)
# We can test if an item exists in the set
>>> 2 in my_set
# Unlike lists, every item in a set must be unique
>>> my_set
>>> my_set.add(3)
>>> my_set
# There is still only one 3 in the set
# my_set should equal {1, 3, 4}
>>> my_other_set = {1, 2, 3}
# We can combine two sets
>>> my_set.union(my_other_set)
# We can get the intersection of two sets
>>> my_set.intersection(my_other_set)
Tuples
Tuples are a lightweight way to hold information that describes something, like a person - their name, age, and hometown. You can think about it kind of like a row in a spreadsheet. Tuples are represented inside parentheses, however parentheses are not required to create a tuple, just a sequence of objects followed by commas.
>>> my_tuple = 1,
>>> my_tuple
# Let's add to our tuple
>>> my_tuple[1] = 2
Oops! Remember that tuples are immutable, so you can't change them once they've been created. Tuples are great for moving data around in a lightweight way, because you can unpack them easily into multiple variables.
>>> person = ('Jim', 29, 'Austin, TX')
>>> name, age, hometown = person
>>> name
>>> age
>>> hometown
Dictionaries
Dictionaries are great for storing data that you can index with keys. The keys must be unique, and the dictionaries are stored in the order you inserted items, however this is only guaranteed as of Python 3.7.
>>> my_dict = {"key": "value"}
# Remember, dictionaries don't have numerical indexes like lists, so if you try to use an index number...
# Unless 0 happens to be a key.
>>> my_dict[0]
# You'll get a KeyError!
# Let's put some more things into our dictionary
>>> my_dict["hello"] = "world"
>>> my_dict["foo"] = "bar"
>>> my_dict
# What was the value for "hello" again?
>>> my_dict["hello"]
# You can also use get() to get a key
>>> my_dict.get("hello")
# What if the key you want doesn't exist?
>>> my_dict["baz"]
# If you're not sure if a key exists, you can ask:
>>> "baz" in my_dict
# Or you can use a default value. If "baz" doesn't exist, return "default response":
>>> my_dict.get("baz", "default response")
# Let's try separating the dictionary into lists of keys and values:
>>> my_dict.keys()
>>> my_dict.values()
# What if we want to get both the key and value pairs? Then we need the dictionary's items. We can use the items() function to get a list of tuples:
>>> my_dict.items()
Mutability
Remember, in Python, some data types are immutable – that means that once they're created, their contents can't be changed. Tuples are immutable - once you make one, you can't alter it, you can only make a new one. Conversely, lists, dictionaries, and sets are mutable - you can change them without making new ones.
Let's see this in practice:
# Lists are mutable
>>> my_list = [1, 2, 3]
>>> my_list[0] = 'a'
>>> my_list
# Dictionaries are also mutable
>>> my_dict = {"hello": "world"}
>>> my_dict["foo"] = "bar"
>>> my_dict
# Sets are mutable, but don't support indexing or item assignment, so you have to use add() and remove()
>>> my_set = {1, 2, 3}
>>> my_set[0] = 'a' # This will throw a TypeError
>>> my_set.add('a')
>>> my_set
# Tuples are immutable
>>> my_tuple = (1, 2, 3)
>>> my_tuple[0] = 'a' # This will throw a TypeError
Solutions
Here's what you should have seen while working through the exercises.
Sets
Here's what you should have seen in your REPL:
>>> my_set = {}
>>> type(my_set)
<class 'dict'>
>>> my_set = set()
>>> type(my_set)
<class 'set'>
>>> my_set = {1, 2, 3}
>>> my_set.add(4)
>>> my_set.remove(2)
>>> 2 in my_set
False
>>> my_set
{1, 3, 4}
>>> my_set.add(3)
>>> my_set
{1, 3, 4}
>>> my_other_set = {1, 2, 3}
>>> my_set.union(my_other_set)
{1, 2, 3, 4}
>>> my_set.intersection(my_other_set)
{1, 3}
Tuples
Here's what you should have seen in your REPL:
>>> my_tuple = 1,
>>> my_tuple
(1,)
>>> my_tuple[1] = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> person = ('Jim', 29, 'Austin, TX')
>>> name, age, hometown = person
>>> name
'Jim'
>>> age
29
>>> hometown
'Austin, TX'
Dictionaries
Here's what you should have seen in your REPL:
>>> my_dict = {"key": "value"}
>>> my_dict[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 0
>>> my_dict["hello"] = "world"
>>> my_dict["foo"] = "bar"
>>> my_dict
{'key': 'value', 'hello': 'world', 'foo': 'bar'}
>>> my_dict["hello"]
'world'
>>> my_dict.get("hello")
'world'
>>> my_dict["baz"]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'baz'
>>> "baz" in my_dict
False
>>> my_dict.get("baz", "default response")
'default response'
>>> my_dict.keys()
dict_keys(['key', 'hello', 'foo'])
>>> my_dict.values()
dict_values(['value', 'world', 'bar'])
>>> my_dict.items()
dict_items([('key', 'value'), ('hello', 'world'), ('foo', 'bar')])
Mutability
Here's what you should have seen in your REPL:
>>> my_list = [1, 2, 3]
>>> my_list[0] = 'a'
>>> my_list
['a', 2, 3]
>>> my_dict = {"hello": "world"}
>>> my_dict["foo"] = "bar"
>>> my_dict
{'hello': 'world', 'foo': 'bar'}
>>> my_set = {1, 2, 3}
>>> my_set[0] = 'a' # This will throw a TypeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'set' object does not support item assignment
>>> my_set.add('a')
>>> my_set
{1, 2, 3, 'a'}
>>> my_tuple = (1, 2, 3)
>>> my_tuple[0] = 'a' # This will throw a TypeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment