7.2 — Nested data structures, Comprehensions, Modules

List of dictionaries

A dictionary can be used to represent a data record such as student record in a course in the following example.

A list of such dictionaries can store multiple data records.

1student_records = [
2 {"name": "Student-A", "ID": 2601234, "grades": [90, 95, 100]},
3 {"name": "Student-B", "ID": 2601000, "grades": [95, 95, 97]},
4 {"name": "Student-C", "ID": 2605000, "grades": [80, 85, 90]}
5]
6
7# Each "grades" list has grades for 3 assignments.

What are assignment 1 grades for “Student-B” ?

1student_records = [
2 {"name": "Student-A", "ID": 2601234, "grades": [90, 95, 100]},
3 {"name": "Student-B", "ID": 2601000, "grades": [95, 95, 97]},
4 {"name": "Student-C", "ID": 2605000, "grades": [80, 85, 90]}
5]
6
7print(student_records[1]["grades"][0]) # 95

Display total grade for each student.

1student_records = [
2 {"name": "Student-A", "ID": 2601234, "grades": [90, 95, 100]},
3 {"name": "Student-B", "ID": 2601000, "grades": [95, 95, 97]},
4 {"name": "Student-C", "ID": 2605000, "grades": [80, 85, 90]}
5]
6
7for record in student_records:
8 name, total_grade = record["name"], sum(record["grades"])
9 print(f"Total grade for {name} is {total_grade}")
Output
Total grade for Student-A is 285
Total grade for Student-B is 287
Total grade for Student-C is 255

Comparing data structures

Data structures — list, tuples, sets and dictionaries can be compared for equality using == and != operators.

1# Lists, order matters
2grades1 = [85, 80, 100]
3grades2 = [85, 80, 100]
4grades3 = [85, 100, 80]
5
6# True only when all elements are equal in order
7print(grades1 == grades2) # True
8print(grades2 == grades3) # False

1# Comparing tuples, order matters
2point1 = (1, 1, 2)
3point2 = (1, 2, 1)
4print(point1 != point2) # True
5
6
7# Comparing sets, order does not matter
8fruits1 = {"apple", "orange", "banana"}
9fruits2 = {"orange", "apple", "banana"}
10
11# True only when sets are of equal length and
12# both sets contain same elements
13print(fruits1 == fruits2) # True
14print(fruits1 == {"apple", "orange", "banana", "grapes"}) # False

1# Comparing dictionaries, order does not matter
2phonebook1 = {"A": 5140001000, "B": 5140002000, "C": 5140003000 }
3phonebook2 = { "B": 5140002000, "A": 5140001000, "C": 5140003000 }
4
5# True only when dictionaries are of equal length and
6# both contain same key-value pairs
7print(phonebook1 == phonebook2) # True
8
9print(phonebook1 == {"A": 4381001000, "B": 5140002000,
10 "C": 5140003000 }) # False

Comparison works for nested structures as well

1points1 = [(1, 1), (2, 10)]
2points2 = [(1, 1), (2, 10)]
3print(points1 == points2) # True
4
5print(points1 == [(1, 1), (2, 5)]) # False
6print(points1 == [(1, 1), [2, 10]]) # False
7
8student1 = {"name": "Student-B", "grades": [90, 100, 100]}
9student2 = {"name": "Student-B", "grades": [90, 100, 90]}
10print(student1 == student2) # False

enumerate function

1mylist = [10, 50, -3.14, 5]
2print(enumerate(mylist))
3# <enumerate object at 0x10e327100>
4
5# enumerate creates an iterable of tuples (index, element),
6# which we convert to list
7list_of_tuples = list(enumerate(mylist))
8print(list_of_tuples)
9# [(0, 10), (1, 50), (2, -3.14), (3, 5)]

1mylist = [10, 50, -3.14, 5]
2
3for i in range(len(mylist)):
4 num = mylist[i]
5 print(i, num)
1mylist = [10, 50, -3.14, 5]
2
3for i, num in enumerate(mylist):
4 print(i, num)

List, set and dictionary comprehensions

We often find ourselves repeating the following pattern to create a list.

Python provides a simpler way to create a list using list comprehension.

1some_list = []
2for i in some_iterable:
3 some_list.append(some_expression)
4
5# Using list comprehension
6some_list = [some_expression for i in some_iterable]

1even_nums = [i for i in range(2, 20, 2)]
2print(even_nums)
3# [2, 4, 6, 8, 10, 12, 14, 16, 18]
4
5
6# Need not use loop variable i in the expression
7zeros = [0 for i in range(7)]
8print(zeros)
9# [0, 0, 0, 0, 0, 0, 0]

1import math
2
3sine_values = [math.sin(x) for x in [-math.pi/2, 0, math.pi/2]]
4print(sine_values)
5# [-1.0, 0.0, 1.0]
6
7string = "10.0,20.5,100.123"
8numbers = [float(word) for word in string.split(",")]
9print(numbers)
10# [10.0, 20.5, 100.123]

Using if-statement in list comprehension

1squares_of_odds = []
2for x in range(1, 20):
3 if x % 2 != 0:
4 squares_of_odds.append(x * x)
5
6print(squares_of_odds)
7# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]
8
9# same as above but using list comprehension
10squares_of_odds = [x * x for x in range(1, 20) if x % 2 != 0]
11print(squares_of_odds)
12# [1, 9, 25, 49, 81, 121, 169, 225, 289, 361]

Set and dictionary comprehension

1odd_squares = {i*i for i in range(1, 20, 2)}
2print(odd_squares)
3# {1, 121, 225, 289, 9, 169, 361, 81, 49, 25}
4
5
6names = ["A", "B", "C", "D"]
7names_to_index = {name: i for i, name in enumerate(names)}
8print(names_to_index)
9# {'A': 0, 'B': 1, 'C': 2, 'D': 3}

Writing & importing modules

  • What are modules exactly?
    • A module is simply a Python file containing definitions and statements.
    • Every .py file is a module. The name of the module is the name of the file.
  • Name of a Python file (module) must follow same rules as variable names.
    • Module names can only start with letters a-z, A-Z or an underscore and must only contain these letters, digits and underscores.

Download files-7.2.zip from Ed Lessons. It contains geometry.py and geometry_tester.py

In geometry_tester.py file, we import and use the functions defined in the module geometry:

1# Import functions from the module
2from geometry import euclidean_distance, sine
3
4# Call the sine function
5print(sine(90))
6
7dist = euclidean_distance((1, 1), (2, 3)) # tuples
8print(dist) # 2.23606797749979

Some observations

  • When we import a module, all code inside that module is executed.
  • Add some statement such as print("hello") in geometry module outside the functions. Then,
    • Run the geometry.py as the main program
    • Run another program which imports the module geometry.

Running a file as main program vs importing it

  • Sometimes, we may want to run some code only when a Python file is executed directly as main program but not when it is imported as a module.
  • For example, suppose we want the following test cases in geometry.py
    1print(sine(-90))
    2print(sine(180))
  • How to make sure the test cases do not execute when geometry is imported as a module?

__name__

  • __name__ is a special variable that the interpreter initializes whenever it executes a file.
  • When a module is executed, the interpreter does the following:
    • sets the value of __name__ for that module using the filename
    • executes all the code in the module.
  • Each module has its own __name__ variable.
  • Add print(__name__) in geometry.py and import geometry module in another program.

"__main__"

  • When we execute a file as the main program, then the variable __name__ is set to be "__main__"
  • Run geometry.py directly and see what value of __name__ is printed.

How to not execute code in a module when it is imported

Add the following at the end of the file geometry.py:

1if __name__ == "__main__":
2 # Run the following code only when this file is
3 # executed as main program but not when it is imported
4 print("hello from geometry!")
5 print(sine(-90))
6 print(sine(180))

Time for some problems on Ed Lessons.