top of page

Beginners: Chapter 3 - Python Collections: Working with Lists, Tuples, Sets, and Dictionaries | The GPM


Python includes several built in collection types that make it easy to group, organize, and process data. The most commonly used are lists, tuples, sets, and dictionaries, each suited to slightly different problems in real projects.​

Lists: Flexible Ordered Sequences

Lists represent ordered collections of items that can be changed after creation. They are ideal when you need to add, remove, or reorder elements frequently.​

You create a list by writing elements in square brackets separated by commas, for example: fruits = ["apple", "banana", "orange", "apple"]. You can also start with an empty list using my_list = []. Access items by index, where the first element is at position 0, as in print(fruits) or use negative indexes like fruits[-1] to read from the end. Slicing lets you take sublists, such as fruits[1:3] for elements at indexes 1 and 2, fruits[::2] for every second element, and fruits[::-1] to see the list in reverse order.​

Lists are mutable, so you can change them in place. To add items, use fruits.append("grape") to place a value at the end, fruits.insert(1, "kiwi") to insert before index 1, or fruits.extend(["pear", "mango"]) to add several values at once. To remove elements, fruits.remove("apple") deletes the first matching value, fruits.pop(1) removes and returns the item at index 1, and del fruits deletes by index without returning anything.​

Sorting and counting are common operations. The call fruits.sort() rearranges the list in place, while sorted_fruits = sorted(fruits) returns a new sorted list without touching the original. You can reverse the order with fruits.reverse(), find the first position of a value using fruits.index("banana"), and count occurrences via fruits.count("apple").​

List comprehensions provide a concise way to generate lists from existing iterables. For example, squares = [x**2 for x in range(10)] builds a list of square numbers, evens = [x for x in range(20) if x % 2 == 0] collects even integers, and lengths = [len(word) for word in fruits] measures each string. For nested data, a matrix like matrix = [,, ] can be flattened with flattened = [item for row in matrix for item in row].​​

When copying lists, it is better to use shallow_copy = fruits.copy() or shallow_copy = list(fruits) instead of assigning another name to the same list. For structures containing nested lists, import copy and use deep_nested = copy.deepcopy(matrix) to avoid unexpected side effects when modifying inner elements.​

Tuples: Reliable Immutable Sequences

Tuples look similar to lists but are immutable, meaning their contents cannot be changed after creation. They are written with parentheses and are useful for fixed collections of values such as coordinates or database rows.​

A typical tuple might be point = (10, 20) or colors = ("red", "green", "blue"). Single element tuples need a trailing comma, for example single = (42,). Values are accessed with the same indexing and slicing syntax as lists, so point returns 10 and colors[1:3] returns ("green", "blue").​

Tuples work well with unpacking, which allows you to assign multiple variables at once. For instance, x, y = point assigns 10 to x and 20 to y, while r, g, b = colors assigns each color to a separate name. Functions often return tuples so that callers can easily capture several values in a single line such as total, count, avg = get_stats(numbers).​

Because they are immutable, tuples can act as dictionary keys and set elements where lists cannot. For more readable structured tuples, you can use collections.namedtuple, for example Point = namedtuple("Point", ["x", "y"]); p = Point(10, 20); then p.x and p.y behave like attribute access on a lightweight object. Tuples also support lexicographic comparison, so (1, 2) < (1, 3) evaluates to True because it compares elements from left to right until it finds a difference.​

Sets: Unique Unordered Collections

Sets represent collections of unique items without any guaranteed order. They are perfect for tasks involving membership tests, removing duplicates, and working with mathematical set operations.​

You can build a set directly using unique_nums = {1, 2, 3, 2, 1}, which automatically stores {1, 2, 3}, or create an empty one with empty_set = set(). Since sets are unordered, they do not support indexing or slicing like lists or tuples.​

Basic operations include adding and removing elements. Write unique_nums.add(4) to insert a new value, unique_nums.remove(1) to delete an item while raising an error if it does not exist, or unique_nums.discard(5) to attempt removal without raising an exception if the value is missing.​

Mathematical set operations are a major strength. The intersection of {1,2,3} and {2,3,4} is {2,3} using {1,2,3} & {2,3,4}, the union of {1,2} and {2,3} is {1,2,3} using {1 {1,2} | {2,3}, and the difference of {1,2,3} minus {2} is {1,3} with {1,2,3} - {2}.​ The symmetric difference, which contains elements in one set or the other but not both, can be found using {1,2} ^ {2,3} to get {1,3}.​

Subset and superset checks determine relationships between sets. For example, {1,2} <= {1,2,3} evaluates to True because every element of the first set appears in the second one, and {1,2,3} >= {1,2} is True for the reverse relationship.​

To remove duplicates from a list, convert it to a set: unique_fruits = set(fruits). When you need an immutable version for use as a dictionary key or set element, call frozen = frozenset(unique_fruits).​

Dictionaries: Fast Key–Value Mappings

Dictionaries store pairs of keys and values, making them one of the most powerful and flexible data types in Python. They are ideal whenever you need to look up values by name instead of by numeric index.​

You can define a dictionary like person = {"name": "Alice", "age": 30, "city": "NYC"} or start with an empty one using empty_dict = {}. Keys must be immutable types such as strings, numbers, or tuples, and each key in a dictionary is unique.​

To read values, use person["name"] to get "Alice" or person.get("age", 0) to safely retrieve the age or a default value if the key is missing. Adding or updating data is as simple as person["job"] = "Engineer" to create a new key or person["age"] = 31 to overwrite the existing value. You can delete entries with del person["city"] or capture and remove a value in one step using popped = person.pop("age").​

Iteration over dictionaries supports different views. Looping with for key in person goes through keys, for value in person.values() goes through values, and for key, value in person.items() lets you work with both at once. This pattern is common for printing details or building derived structures.​

Dictionary comprehensions offer concise construction and filtering. For instance, squares = {x: x**2 for x in range(5)} creates a mapping from each number to its square, and filtered = {k: v for k, v in person.items() if v != "NYC"} removes entries with a particular value. For counters and automatic default values, collections.defaultdict is helpful, for example scores = defaultdict(int); scores["Alice"] += 10 works without checking if the key exists first.​

Nested dictionaries represent structured data such as company = {"employees": {"Alice": {"dept": "IT", "salary": 75000}, "Bob": {"dept": "HR", "salary": 65000}}}. Accessing nested values is straightforward using company["employees"]["Alice"]["salary"], and this pattern is common when working with JSON data from web APIs.​

Practical Patterns and Best Practices

Real programs often combine these four collection types to model more complex scenarios. For example, an inventory system might store a list of items for each category, use a set for unique tags, and keep prices and stock levels in dictionaries keyed by product name.​

Suppose you have an inventory like this: inventory = {"fruits": ["apple", "banana", "apple"], "unique_tags": {"organic", "sale"}, "prices": {"apple": 1.50, "banana": 0.75}, "stock": (100, 50)}. Here the list allows duplicate product names in a category, the set holds unique properties, the dictionary maps products to prices, and the tuple stores fixed stock information for two warehouses.​

Another common pattern is counting occurrences using dictionaries or specialized tools. For instance, to build an average grade per student, you can accumulate scores in a dictionary of lists and then compute averages with a dictionary comprehension: grades = {}; for student, score in data: if student not in grades: grades[student] = []; grades[student].append(score); averages = {student: sum(scores)/len(scores) for student, scores in grades.items()}.​

Choosing the right collection has performance implications. Lists are good for ordered data and efficient index based access, but searching for a value is O(n) in the worst case. Sets and dictionaries use hashing so membership tests and key lookups are O(1) on average, making them far better for large collections where you frequently check whether a value is present. Tuples give you list like behavior with the added benefit of immutability and their ability to serve as keys in dictionaries.​

When you design data structures in Python, think about whether you need ordering, uniqueness, mutability, or key based access. Lists and tuples are best when order matters, sets are ideal when uniqueness and membership checks are primary, and dictionaries are essential when you want to associate names or identifiers with values. Combining them thoughtfully leads to clean, efficient, and maintainable code in real world applications.​


Disclosure:

  • As an Amazon Associate I earn from qualifying purchases.

  • We may earn a commission when you buy through links on our site, at no extra cost to you.


Check out some great offers below: 


Comments


Subscribe to Our Newsletter

  • Image by Mariia Shalabaieva
  • Instagram
  • Facebook

© 2025 - Powered and secured by TheGPM. All rights reserved.

bottom of page