Solutions Encaptiolation exercises

Ex 1: Car object

 1# car.py (solution)
 2
 3# 1. Car object
 4# Create a Car class. 
 5# When instanciated the object should be able to take 4 attributes (Make, Model, bhp, mph).
 6# They all 4 should be properties.
 7
 8db = {
 9    "Audi": {"A3", "A4", "A6", "Q5", "Q7"},
10    "BMW": {"3 Series", "5 Series", "7 Series", "X3", "X5"},
11    "Mercedes-Benz": {"C-Class", "E-Class", "S-Class", "GLC", "GLE"},
12    "Volkswagen": {"Golf", "Passat", "Polo", "Tiguan", "Arteon"},
13    "Volvo": {"S60", "S90", "V60", "XC60", "XC90"},
14    "Toyota": {"Corolla", "Camry", "RAV4", "Prius", "Hilux"},
15    "Honda": {"Civic", "Accord", "CR-V", "Fit", "HR-V"},
16    "Ford": {"Focus", "Fiesta", "Mustang", "Escape", "Explorer"},
17    "Nissan": {"Altima", "Rogue", "Sentra", "Maxima", "Murano"},
18    "Mazda": {"Mazda3", "Mazda6", "CX-5", "CX-9", "MX-5"}
19}
20
21
22class Car:
23    def __init__(self, make, model, bhp, mph):
24        self.make = make
25        self.model = model
26        self.bhp = bhp
27        self.mph = mph
28
29    # make
30    @property 
31    def make(self):
32        return self._make
33    
34    @make.setter
35    def make(self, make):
36        if make not in db.keys():
37            raise ValueError('Make is not in valid list')
38        self._make = make
39
40    # model
41    @property 
42    def model(self):
43        return self._model
44    
45    @model.setter
46    def model(self, model):
47        if model not in db[self.make]: 
48            raise ValueError(f'No model named {model} belongs to {self.make}')
49        self._model = model
50    
51    # bhp
52    @property 
53    def bhp(self):
54        return self._bhp
55    
56    @bhp.setter
57    def bhp(self, bhp):
58        if bhp < 0:
59            raise ValueError('bhp can not be negative')
60        self._bhp = bhp
61
62    # mph
63    @property 
64    def mph(self):
65        return self._mph
66    
67    @mph.setter
68    def mph(self, mph):
69        if mph < 0:
70            raise ValueError('mph can not be negative')
71        self._mph = mph
72

Ex 2: Bank

 1"""
 22. Bank 
 3In the exercise from last monday with the bank, account and customer, change the code to use properties instead of the public variables.  
 4The bank class should futher be change into not taking any accounts as parameters at initialization. The accouts should be added afterwards, eithers as a single account one at a time, or as a collection of accounts (many at the sametime).      
 5Somewhere you should change the code so that a customer only can create one account.     
 6The Customer class should make sure that the customer is over 18 year of age.
 7
 8"""
 9
10class Bank:    
11    def __init__(self):
12        self._accounts = []  # When bank is initialized it has  the abillity to hold many accounts
13    
14    @property
15    def accounts(self):
16        return self._accounts
17
18    @accounts.setter
19    def accounts(self, acc):
20        if type(acc) in [list, tuple, set]:
21            for i in acc:
22                self._has_account(i)
23                self._accounts.append(i)
24        else:
25            self._has_account(acc)
26            self._accounts.append(acc)
27
28    def _has_account(self, acc):
29        for i in self._accounts:
30            if acc.cust.name == i.cust.name:
31                raise ValueError('Customer aleready has an account!')
32        return False
33    
34    def __repr__(self):
35        return str(self.__dict__)
36
37class Account:
38    def __init__(self, no, cust):
39        self.no = no
40        self.cust = cust
41
42    def __repr__(self):
43        return str(self.__dict__)
44
45
46class Customer:
47    def __init__(self, name, age):
48        self.name = name
49        self.age = age
50
51    @property
52    def age(self):
53        return self._age
54
55    @age.setter
56    def age(self, age):
57        if age < 18:
58            raise ValueError('You must be 18 or above to create an account')
59        
60        self._age = age     # create a variable with the name _age
61
62    def __repr__(self):
63        return str(self.__dict__)

Ex 3: Machine -> printer

 1# printer.py (solution)
 2
 3"""
 4    3. Machine -> printer
 5    Create a Machine class that takes care of powering on and off a the machine.   
 6    Create a printer class that is a subclass of the Machine super class.   
 7    The printer should be able to print to console.  
 8    The printer should have a papertray, which should be in its own class. The papertray class should keep track of the paper, it should have the abillity to use paper and and load new paper in the tray if empty.  
 9"""
10
11class Machine:
12    """ takes care of turning on and off  """
13
14    def __init__(self):
15        self._is_on = False  # one _ = protected (to be used only in subclasses) 
16    
17    def power(self):
18        self._is_on = not self._is_on
19
20class Printer(Machine):
21    def __init__(self):
22        Machine.__init__(self)
23        self._pt = Papertray()
24
25    def print(self, text):
26        if self._pt.paper == 0:
27            print('Papertray is empty')
28        else:
29            if self._is_on:
30                print(text)
31                self._pt.paper = self._pt.paper - 1
32            else:
33                print('Printer is off')
34
35    @property
36    def load(self):
37        return self._pt.paper
38
39    @load.setter
40    def load(self, no):
41        self._pt.paper = no
42
43class Papertray:
44    def __init__(self):
45        self.paper = 2
46
47    @property
48    def paper(self):
49        return self._paper
50
51    @paper.setter
52    def paper(self, paper):
53        self._paper = paper
54
55
56
57
58
59
60
61
62

Ex 4: Rectangle

 1# Your code should include:
 2# A class called Rectangle
 3# width and height attributes with property decorators
 4# A get_area method that calculates the area of the rectangle
 5# Appropriate error handling for non-positive width and height values
 6
 7class Rectangle:
 8    def __init__(self, width, height):
 9        self.width = width
10        self.height = height
11
12    @property
13    def width(self):
14        return self._width
15    
16    @width.setter
17    def width(self, width):
18        if width < 0:
19            raise ValueError('Witdh can not be negative')
20        self._width = width
21    
22    @property
23    def height(self):
24        return self._height
25    
26    @height.setter
27    def height(self, height):
28        if height < 0:
29            raise ValueError('Height can not be negative')
30        self._height = height
31
32    @property
33    def area(self):
34        return (self.height + self.width)**2

Ex 5: Color converter

 1class Color:
 2    def __init__(self, r, g, b):
 3            self.r = r
 4            self.g = g
 5            self.b = b
 6
 7    @property
 8    def hex(self):
 9        return f'#{hex(self.r)[2:]}{hex(self.g)[2:]}{hex(self.b)[2:]}'
10