Closure#

Syllabus Week 11: Classes II#

  • Inheritance: parent class (superclass) and child class (subclass)

  • Method overriding

  • The super() function

  • Method __str__() and overloading str() and print()

  • Operator overloading example on __add__() and __eq__()

Advanced#

Note about the material in Advanced section. The advanced material contains more some additional topics related to the weeks’s content. You are not required to read this material, and none of the exercises or exam questions will rely on this material.

Advanced 11.1: More Overloading#

Here is a class with some more operator overloading.

class BaseMetricUnit:
    
    def __init__(self, value: float, unit_name: str):
        self.value = value
        self.unit_name = unit_name
    
    def __str__(self):
        return f"{self.value} {self.unit_name}"
    
    def __add__(self, other):
        if type(other) == type(self):
            return BaseMetricUnit(self.value + other.value, self.unit_name)
    
    def __sub__(self, other):
        if type(other) == type(self):
            return BaseMetricUnit(self.value - other.value, self.unit_name)
    
    def __mul__(self, other):
        return BaseMetricUnit(self.value * other.value, f"{self.unit_name} {other.unit_name}")

    def __eq__(self, other):
        return (self.unit_name == other.unit_name) and (self.value == other.value)
       

Let’s use it to define second an instance of the BaseMetricUnit, with the name 's'.

my_second = BaseMetricUnit(10, 's')
other_second = BaseMetricUnit(20, 's')
print(my_second)
print(other_second)
print(my_second + other_second)
print(my_second - other_second)

We would probably like to have specific classes for each unit. Check the code below.

class KG(BaseMetricUnit):
    def __init__(self, value: float):
        unit_name = 'Kg'
        super().__init__(value, unit_name)
        
class Time(BaseMetricUnit):
    def __init__(self, value: float):
        unit_name = 's'
        super().__init__(value, unit_name)

class Distance(BaseMetricUnit):
    def __init__(self, value: float):
        unit_name = 'm'
        super().__init__(value, unit_name)
    

What do you think will be printed by the following code?

weight = KG(10)
length = Distance(2)
print(weight)
print(length)
print(weight + weight)
print(weight + length)
print(weight * length)
print(weight == length)

Is the result of area + weight as you would expect it?

We have defined weight + weight and a product of two objects. Test what do you get if you ask for 2 * weight? What do you get if you ask for weight * 2? Clearly, our class can be improved.

Advanced 11.2: Smart Object Creation#

In the ScoreTracker task, you overrode the __add__ method. Later, when you defined UniqueScoreTracker, it would had been nice to inherit the __add__ method, but the returned object was always ScoreTracker because of the assignment combined = ScoreTracker(), where we assume that you called the new object combined. Instead of always creating ScoreTracker, it would be useful to create a new object of the same class as self.

It turns out that there is a way to do this. The type function can be used to get the class of an object, and you can call this class to create a new object. So, you should replace the line with combined = type(self)().

Advanced 11.3: More Class Inheritance#

One can also inherit from the build-in classes such as str ,dict , tuple , and list .

Below you can see an implementation of a dict subclass, without any additional functionality. This will currently behave exactly like a regular dictionary would.

class MyDict(dict):
    pass  

my_dict = MyDict({'University': 'DTU'})
print(my_dict)

Try implement addition for this dictionary subclass. You can assume that all values in the dictionary have a + operator implemented for them already. The behavior should be that for all keys they share, we add the corresponding values, and for all keys that they do not share, you create a new key-value pair. The method should return a new instance of MyDict.

Advanced 11.4: Cat Class#

You may find yourself in a situation, where you have a class that e.g. describes each cat you have at home (if you have a lot of cats). Therefore you also have a list of cats, consider the example below.

class Cat:
    def __init__(self, name, age, weight):
        self.name = name
        self.age = age
        self.weight = weight

my_cats = [Cat('Fluffy', 2, 3), Cat('Preben', 1.8, 2.2), Cat('Dummy', 2.1, 2.2)]

You want to know stuff about your cats, and therefore you want to be able to sort them by e.g. their weight. However, you don’t want to implement your own sorting algorithm. You can use the build-in sorted() function, if you overload the < operator for the cat class, by overriding __lt__ (less than) method. The operator should return true if one cat’s weight is less than the others. Also implement the __str__() method, it should show the cat’s name, age and weight. Subsequently, run the sort function on the list.

Advanced 11.5: Class Inheritance#

From the previous exercise you know how to make a class Cat where you can sort the cats by weight. However, you might want to be able to sort them by something other than weight. That could for example be by their age. sImplement a subclass to the cat class, where you override only the < operator such that it sorts by age instead.

Now implement a subclass that enables sorting by the name of the cat, note that the < operator is already defined between strings, so you dont need to implement that.

Advanced 11.6: More Class Inheritance#

One can also inherit from the build-in classes such as str ,dict , tuple , and list .

Below you can see an implementation of a dict subclass, without any additional functionality. This will currently behave exactly like a regular dictionary would.

class MyDict(dict):
    pass  

my_dict = MyDict({'University': 'DTU'})
print(my_dict)

You should now implement addition for this dictionary subclass. You can assume that all values in the dictionary have a + operator implemented for them already. The behaviour should be that for all keys they share, we add the corresponding values, and for all keys that they do not share, you create a new (key,value) pair. The method should return a new instance of MyDict.

Advanced 11.7: Comparing Cats#

Consider the first Cat class from the exercises today. You should now create a subclass of it, that allows for the following sorting rules:

  1. Cat one is smaller than cat two if cat two weighs more than cat one.

  2. If they weigh the same, then cat two is larger if it is older.

  3. If they are of equal age then cat two is larger if its name is shorter than cat one’s.

  4. If their names have equal length, then cat two is larger if its name is alphabetically after cat one’s.