Understanding of an example

Why do I need to use self.price_of_insured_item instead of self.price_of_item?

I think it’s meant to a base price that varies in rate with each context applied to it (vehicle insurance, home insurace).

The naming convention reflects that the price_of_insured_item is going to be different than the actual price_of_item. And though one could name them the same thing, the naming itself can potentially confuse this distinction.

The details of a class that are presented as a public interface do not need to be the same as the internal details of the workings of the class. With the InsurancePolicy example, a documented public interface might present the constructor as follows:

InsurancePolicy(price_of_item)

The price_of_item parameter is a local variable that exists only while the __init__ method is executing. It is unimportant to the user of the class that the information from that variable is saved to an instance variable with a different name.

There are various reasons why a programmer might choose to use instance variables that differ, in some respects, from parameters that are used to initialize them. In the current example, the author of the course does not explain why this was done. Perhaps it was because, as @toastedpitabread mentioned, the context under which the information from price_of_item is passed to the constructor differs from the manner in which is used later.

Following is a simple example of a class, City, in which the information is stored in manner that differs from how it is passed to __init__ during instantiation of the class:

class City:
  def __init__(self, name, first_resident):
    # instantiate with name of City and name of first resident
    self.name = name # name of the City
    # name of the first resident is stored in a list so that more residents can be added later
    self.residents = [first_resident] # append name of first resident
    self.sorted = False # names of residents have not yet been sorted
  def add_resident(self, resident):
    self.residents.append(resident) # append new resident
    self.sorted = False # names of residents might no longer be sorted
  def get_population(self):
    return len(self.residents)
  def __str__(self):
    if not self.sorted:
      # sort residents by last name if they have not yet been sorted
      self.residents.sort(key=lambda name: (name.split()[1], name.split()[0]))
      self.sorted = True # names of residents are now sorted
    return f'City of {self.name}: {", ".join(self.residents)}'

london = City("London", "Michael Palin")
london.add_resident("Terry Jones")
london.add_resident("Eric Idle")
london.add_resident("Terry Gilliam")
london.add_resident("John Cleese")
london.add_resident("Graham Chapman")

print(london)

Output:

City of London: Graham Chapman, John Cleese, Terry Gilliam, Eric Idle, Terry Jones, Michael Palin

Note, in particular, that the name of the first resident of the City is passed as a str in the first_resident parameter. However that information is saved in an instance variable named residents, which is a list. That is so that the names of additional residents can be added later.