10. Konsultacija ‐ Functions, Mutable vs Immutable - MantsSk/CA_PTUA14 GitHub Wiki

Lets practice previous task

With for loops:

num_students = int(input("Enter the number of students: "))

students = []

for _ in range(num_students):
    name = input("Enter student name: ")

    grades = []
    for _ in range(3):
        while True:
            try:
                grade = float(input(f"Enter grade for {name} (0-100): "))
                if 0 <= grade <= 100:
                    grades.append(grade)
                    break
                else:
                    print("Please enter a valid grade between 0 and 100.")
            except ValueError:
                print("Please enter a valid numeric grade.")

    student = {'name': name, 'grades': grades}
    students.append(student)

# Calculate and display average grades
for student in students:
    average = sum(student['grades']) / len(student['grades'])
    print(f"Average grade for {student['name']}: {average:.2f}")

# Find the best student
best_student = students[0]
for student in students:
    if sum(student['grades']) / len(student['grades']) > sum(best_student['grades']) / len(best_student['grades']):
        best_student = student

print(f"\nBest student: {best_student['name']} with an average grade of {sum(best_student['grades']) / len(best_student['grades']):.2f}")

Or you can use while loop, but I would argue that the "for" code looks cleaner. Also not needing to use "break" avoids potential trouble if we miss it and get infinite loops after.

While version (I also added additional comments how you can modify best_student code_

num_students = int(input("Enter the number of students: "))

students = []

student_index = 0
while student_index < num_students:
    name = input("Enter student name: ")

    grades = []
    grade_index = 0
    while grade_index < 3:
        try:
            grade = float(input(f"Enter grade for {name} (0-100): "))
            if 0 <= grade <= 100:
                grades.append(grade)
                grade_index += 1
                break
            else:
                print("Please enter a valid grade between 0 and 100.")
        except ValueError:
            print("Please enter a valid numeric grade.")

    student = {'name': name, 'grades': grades}
    students.append(student)
    student_index += 1

# Calculate and display average grades
for student in students:
    average = sum(student['grades']) / len(student['grades'])
    print(f"Average grade for {student['name']}: {average:.2f}")

def get_average(student):
    return sum(student['grades']) / len(student['grades'])

best_student = students[0]
for student in students:
    if get_average(student) > get_average(best_student):
        best_student = student

print(f"\nBest student: {best_student['name']} with an average grade of {get_average(best_student)}")

# We can not use functions (see below), but there is a lot more code to write :)

# best_student = students[0]
# for student in students[1:]:
#     if sum(student['grades']) / len(student['grades']) > sum(best_student['grades']) / len(best_student['grades']):
#         best_student = student
#
# print(f"\nBest student: {best_student['name']} with an average grade of {sum(best_student['grades']) / len(best_student['grades'])}")

# Note that this you can probavly find even a more optimized way to find this, example:
# def average_grade(student):
#     return sum(student['grades']) / len(student['grades'])
#
# best_student = max(students, key=average_grade)

Repeating functions (practice)

  • Create a mini python program which would take two numbers as an input and would return their sum, subtraction, division, multiplication.

Immutable vs mutable

Strings are immutable example:

# Immutable example with strings
original_string = "Hello"
modified_string = original_string.upper()

# The original string remains unchanged
print("Original String:", original_string)
# Output: Original String: Hello

# The modified string is a new string
print("Modified String:", modified_string)
# Output: Modified String: HELLO

Lists are mutable example

# Mutable example with lists
original_list = [1, 2, 3, 4]
modified_list = original_list

# Modify the list in place
modified_list[0] = 99

# Both variables refer to the same list in memory
print("Original List:", original_list)
# Output: Original List: [99, 2, 3, 4]

print("Modified List:", modified_list)
# Output: Modified List: [99, 2, 3, 4]

Ooh oh, we have a problem. We didn't modify the original list, but it got modified anyway. Imagine how many problems this can cause if this would be a huge program. It would be very hard to debug the issue. What should we do? We can use shallow copies:

import copy

original_list = [1, 2, 3, 4]
shallow_copy = copy.copy(original_list)

# Modify the copy without affecting the original
shallow_copy[0] = 99

print("Original List:", original_list)
# Output: Original List: [1, 2, 3, 4]

print("Shallow Copy:", shallow_copy)
# Output: Shallow Copy: [99, 2, 3, 4]

But that can still cause the problem, if we have this code:

import copy

original_list = [1, [2, 3], 4]
shallow_copy = copy.copy(original_list)

shallow_copy[1][0] = 99

print("Original List:", original_list)
# Output: Original List: [1, [99, 3], 4]

print("Shallow Copy:", shallow_copy)
# Output: Shallow Copy: [1, [99, 3], 4]

An answer would be a deep copy that creates a new object and recursively creates new objects for all the elements within the container:

import copy

original_list = [1, [2, 3], 4]
deep_copy = copy.deepcopy(original_list)

deep_copy[1][0] = 99

print("Original List:", original_list)
# Output: Original List: [1, [2, 3], 4]

print("Deep Copy:", deep_copy)
# Output: Deep Copy: [1, [99, 3], 4]

So what data types are mutable/immutable?

Immutable - Strings, Tuples, Integers, Floats, Booleans

Mutable - Lists, Dictionaries, Sets

Additional Tasks

  • Create a function that returns only strings with unique characters.

  • Create a function that checks whether the provided Lithuanian citizen's code is valid.
  • Make the program generate a valid personal code (using the previously created function) based on the input of gender, date of birth, and sequence number.

Information about the composition of the personal code

Example of a working validator/generator