Scientific Computing with Python Projects - Probability Calculator

Tell us what’s happening:

I was wondering if someone could help me. I have passed the first 2 of the 3 tests for this project and am now trying to solve the experiment function part. I wish to create a variable named check_hat which is a function of hat.contents but leaves hat.contents unchanged when I’ve done what I need to do to check_hat
I’ve ran the code and printed both check_hat and hat.contents and whilst check_hat is exactly what I want it to be, hat.contents changes to be the exact same as check_hat
I’ve included my entire code and would appreciate any help.

Your code so far

import random

class Hat():
  def __init__(self, **colours):
    self.total = 0
    vals = []
    self.contents = []
    index = 0
    for value in colours.values():
      vals.append(value)
      self.total += value
    for colour in colours:
      count = 0
      index += 1
      while count < vals[index - 1]:
        self.contents.append(colour)
        count += 1
    print(self.contents)
  def draw(self, no):
    count1 = 0
    if no > self.total:
      return self.contents
    else:
      while count1 < no:
        self.contents.pop(random.randrange(len(self.contents)))
        count1 += 1
      return self.contents

def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
  check_hat = hat.contents
  for key in expected_balls:
    count2 = 0
    while count2 < expected_balls[key]:
      check_hat.remove(key)
      count2 += 1
  print(check_hat)
  print(hat.contents)  
hat = Hat(black=6, red=4, green=3)
probability = experiment(hat=hat,
                  expected_balls={"red":2,"green":1,'black':3},
                  num_balls_drawn=5,
                  num_experiments=2000)

Your browser information:

User Agent is: Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36

Challenge Information:

Scientific Computing with Python Projects - Probability Calculator

There are couple concepts meeting here.

This line doesn’t create new list, it just adds another variable which points to the same list. Therefore not only check_hat == hat.contents, but check_hat is hat.contents.

It’s actually the same not only for lists, for example:

num = 2
num2 = num

print(num is num2)  # True

If hat.contents would be later set to another list, it might have work, for example:

check_hat = hat.contents
hat.contents = []
print(check_hat is hat.contents)  # False

But that cannot be done in the experiment, as the check_hat will only be mutated. Mutability of the data type is affecting this greatly. There’s couple of ways to create truly new list, from other list:

l = [1, 2, 3]
l2 = list(l)
print(l is l2)  # False
print(l2)       # [1, 2, 3]
l3 = l[:]
print(l is l3)  # False

However there can be more complicated cases, when list contains objects of data type that are mutable. For example list of lists, or list of dictionaries. While the approach above would create new list, the actual contents would still be pointing to the same objects. Doing that for such case would create a shallow copy. To have a deep copy would be required to make not shallow copies of the list contents as well. Python has helpful copy module, which has deepcopy function which will take care of creating deep copy of object. See copy — Shallow and deep copy operations — Python 3.12.2 documentation

Thank you for your help. An excellent explanation.