Saturday 7 August 2021

Python crash course part 10: inheritance and polymorphism

In the last part we've shown how to create and use a class in Python. Today we're going to talk about inheritance: wchich means creating sub-classes, which add different behaviour to the class they inherit from (or extended class, in Java nomenclature).

Suppose we have a class like this:

class Runner:
    def __init__(self):
        self.shoes = True
    def hasShoes(self):
        return self.shoes

We can create an object of this class and call the method hasShoes on it:

# !/usr/bin/python3

class Runner:
    def __init__(self):
        self.shoes = True
    def hasShoes(self):
        return self.shoes

def main():

    myRunner = Runner()
    print(myRunner.hasShoes())

if __name__ == "__main__":
    main()

The code results in:

True

Now let's extend this class by creating a subclass (LongDistanceRunner) and adding a method there - and let's create an instance of subclass and call this method:

# !/usr/bin/python3

class Runner:
    def __init__(self):
        self.shoes = True
    def hasShoes(self):
        return self.shoes

class LongDistanceRunner(Runner):
    def run(self):
            if self.hasShoes():
                print("Running slowly")
            else:
                print("Not running (no shoes)")

def main():

    myRunner = Runner()
    print(myRunner.hasShoes())

    ldRunner = LongDistanceRunner()
    ldRunner.run()
    print(ldRunner.hasShoes())


if __name__ == "__main__":
    main()

The code results in:

True
Running slowly
True

As we can see, we can call both run() - which is method from the subclass (LongDistanceRunner) and hasShoes() which is method defined in superclass ( Runner ) on the object of class LongDistanceRunner. What' more - in the class LongDistanceRunner (in the method run() ) we can use our superclass features (in this case: the method hasShoes() ).

Now let's add another class inheriting from Runner :

# !/usr/bin/python3

class Runner:
    def __init__(self):
        self.shoes = True
    def hasShoes(self):
        return self.shoes

class LongDistanceRunner(Runner):
    def run(self):
            if self.hasShoes():
                print("Running slowly")
            else:
                print("Not running (no shoes)")

class ShortDistanceRunner(Runner):
    def run(self):
            if self.hasShoes():
                print("Running fast")
            else:
                print("Not running (no shoes)")
def main():

    myRunner = Runner()
    print(myRunner.hasShoes())

    ldRunner = LongDistanceRunner()
    ldRunner.run()
    print(ldRunner.hasShoes())

    sdRunner = ShortDistanceRunner()

if __name__ == "__main__":
    main()

We can add both our "runners" to a collection and call the method  hasShoes() on both of them:

# !/usr/bin/python3

class Runner:
    def __init__(self):
        self.shoes = True
    def hasShoes(self):
        return self.shoes

class LongDistanceRunner(Runner):
    def run(self):
            if self.hasShoes():
                print("Running slowly")
            else:
                print("Not running (no shoes)")
    def drinkWater(self):
        print("Drinking water (while running)")

class ShortDistanceRunner(Runner):
    def run(self):
            if self.hasShoes():
                print("Running fast")
            else:
                print("Not running (no shoes)")
    def drinkWater(self):
        print("Drinking water (not while running)")
def main():

    myRunner = Runner()
    print(myRunner.hasShoes())

    ldRunner = LongDistanceRunner()
    ldRunner.run()
    print(ldRunner.hasShoes())

    sdRunner = ShortDistanceRunner()

    runners = [ldRunnersdRunner]

    print("Do runners have shoes?")
    for runner in runners:
        print(runner.hasShoes())
        
if __name__ == "__main__":
    main()

Which results in:

True
Running slowly
True
Do runners have shoes?
True
True

What's interesting here is that the hasShoes() method works "polymorphic" here - although it's not defined in the superclass. This behaviour is what differs polymorphism in Python from Java, C# or C++ - in all these languages polymorphism is strictly connected to inheritance.

Tuesday 3 August 2021

Python crash course part 9: basic object-oriented code examples


Today we'll take a look at some examples of object-oriented code in Python.

As I'm learning this topic almost in the same time I'm writing this, I'll do lot of mistakes - especially because some topics seem different than C++/Java/C# style I'm used to.

Ok, let's begin with creating a class with one method, creating an object of this class and calling this method.

# !/usr/bin/python3

class ClassWithMethod:
    def say_something(self):
        print("Elementary!")

def main():
    objectWithMethod = ClassWithMethod()
    objectWithMethod.say_something()

if __name__ == "__main__":
    main()

We define a class ClassWithMethod, with one method: say_something(self) simply printing some text. self param is, as we will see, the reference to the calling object. For now, we just instantiate the class:

    objectWithMethod = ClassWithMethod()

...and call the method:

    objectWithMethod.say_something()

The result of running this code is:

Elementary!

Now let's define other class with one field (or attribute).

class ClassWithField:
    def __init__(selfname):
        self.name = name

To instantiate and use it we can add the following lines to main function:

    objectWithField = ClassWithField("Lestrade")
    print(objectWithField.name)

As we can see, __init__(selfname) works as a constructor with one parameter. The code now prints:

Elementary!
Lestrade

 What about a method? Well, take a look at another class:

class ClassWithFieldAndMethod:
    def tellName(self):
        print(self.name)

If we instantiate it, and set field name to something, we can call our method (in main!):

    objectWithFieldAndMethod = ClassWithFieldAndMethod()
    objectWithFieldAndMethod.name = "John Watson"
    objectWithFieldAndMethod.tellName()

Giving result:

John Watson

What's interesting, we can add fields to a class object on the fly, without declaring them. Add this to main:

    objectWithFieldAndMethod2 = ClassWithFieldAndMethod()
    objectWithFieldAndMethod2.name = "Sherlock Holmes"
    objectWithFieldAndMethod2.tellName()

    # you can add field on the fly...
    # ... to the object!
    objectWithFieldAndMethod2.age = 33 
    
    # you can do this only if field has been added:
    print(objectWithFieldAndMethod2.age)

We create another object (objectWithFieldAndMethod2), add the field named age to it (simply by initializing it) and then we can access it. The code execution result becomes:

Elementary!
Lestrade
John Watson
Sherlock Holmes
33

Please note we've added the age field to our object, not class. It means that if we try to get to this field on our previous object, we get error.

def main():
    objectWithMethod = ClassWithMethod()
    objectWithMethod.say_something()

    objectWithField = ClassWithField("Lestrade")
    print(objectWithField.name)

    objectWithFieldAndMethod = ClassWithFieldAndMethod()
    objectWithFieldAndMethod.name = "John Watson"
    objectWithFieldAndMethod.tellName()

    objectWithFieldAndMethod2 = ClassWithFieldAndMethod()
    objectWithFieldAndMethod2.name = "Sherlock Holmes"
    objectWithFieldAndMethod2.tellName()

    # you can add field on the fly...
    # ... to the object!
    objectWithFieldAndMethod2.age = 33 
    
    # you can do this only if field has been added:
    print(objectWithFieldAndMethod2.age)

    # this results in error!
    print(objectWithFieldAndMethod.age)

...gives:

Elementary!
Lestrade
John Watson
Sherlock Holmes
33
Traceback (most recent call last):
  File "d:\Projekty\Python\controlflow.py", line 71, in <module>
    main()
  File "d:\Projekty\Python\controlflow.py", line 50, in main
    print(objectWithFieldAndMethod.age)
AttributeError: 'ClassWithFieldAndMethod' object has no attribute 'age'

What about private fields? We define it by preceding initialized field name with double underscore:

class ClassWithPrivateFieldAndMethod:
    def setName(selfname):
        self.__name = name

We can create an object of this class and call the method to set field __name:

    objectWithPrivateField = ClassWithPrivateFieldAndMethod()
    objectWithPrivateField.setName("Mary Watson")

..but we can't do this:

 print(objectWithPrivateField.__name)

... as the program will behave as such attribute doesn't exist:

  File "d:\Projekty\Python\controlflow.py", line 71, in <module>
    main()
  File "d:\Projekty\Python\controlflow.py", line 55, in main
    print(objectWithPrivateField.__name)
AttributeError: 'ClassWithPrivateFieldAndMethod' object 
has no attribute '__name'

BUT in fact, we can access this field - as "private" in Python does not mean the attribute is unaccesible - it rather means it should not be accessed. So we can access this attrbute using the following syntax: objectName._ClassName__field_name (please note the underscores, one before class name and two before field  name):

print(objectWithPrivateField._ClassWithPrivateFieldAndMethod__name)

This gives us:

Mary Watson

Now let's take a look at a param passed in the constructor.

The following class:

class ClassWithParamaterInConstructor:
    def __init__(selfname):
        self.name = name

...can be created like this:

   objectConstructedWithParam = ClassWithParamaterInConstructor("Moriarty")

We can then normally access the name field, so:

   print(objectConstructedWithParam.name)

...gives us this ominous result:

Moriarty

The last thing: static (class) fields. They're defined simply in class body, not in the methods:

class ClassWithStaticField:
    field = 40

But beware: they can be accessed with both class name and object name. In the first case, they behave like static. In hte latter - they're simply fields, initialized with given value.

An example should give us more light on this:

    stat1 = ClassWithStaticField()
    stat2 = ClassWithStaticField()
    stat2.field = 41
    print(stat1.field# object's field default value is 40
    print(stat2.field# this object's field value is 41
    print(ClassWithStaticField.field# static field value is still 40!

... which code gives us:

40
41
40
40

We create two objects of class ClassWithStaticFieldstat1 and stat2. Then we assign value 41  to the field field of the first object. It changes this instance's value, but the field field of object stat1 stays the same! What's more - the ClassWithStaticField.field is also another reference. We can change it, of course, by calling:

    ClassWithStaticField.field = 42

  So the code:

    stat1 = ClassWithStaticField()
    stat2 = ClassWithStaticField()
    stat2.field = 41
    print(stat1.field# object's field default value is 40
    print(stat2.field# this object's field value is 41
    print(stat1.field# object's field default value is 40
    
    print(ClassWithStaticField.field# static field value is still 40!
    ClassWithStaticField.field = 42
    print(ClassWithStaticField.field# not anymore!

results in:

40
41
40
40
42

Confusing? Well, for me it certainly is ;D The full program  listing illustrating the concepts above looks as follows:


... and gives the following result:

Elementary!
Lestrade
John Watson
Sherlock Holmes
33
Mary Watson
Moriarty
40
41
40
40
42

I hope it happens to be useful for some of you :)

Sunday 1 August 2021

Android Java to Kotlin migration by example part 13: operator overloading

This time again we're going to be talking about something available in Kotlin, but not in Java: operator overloading.

Operator overloading means that we can apply operators, like + (someobject+anotherone) or [ ] (someobject[5]) to our objects. Expression like this (someobject + anotherone) can be thought of as methods: it's just another way of expressing someobject.plus(anotherone)someobject[5] would mean "call the method [] with parameter 5 on someobject.  

Altough operator overloading is an old concept (it's common in C++, where - for example - the famous cout<<"Something"; actually uses overloaded left bit shift operator for object cout of class ostream; it's also available in C# and Python), it's not implemented in Java (which was a deliberate choice of its creators).

Still, it can be very useful and Kotlin allows it.

Let's get to our code. I think the most basic example of operator overloading is use of class representing complex numbers (the code below is the full program, with both class definition and it's use):

package com.mypackage

import kotlin.Exception
import kotlin.math.sqrt

class Complex(var re: Float, var im: Float){

fun modulus(): Float{ // just a method
return sqrt(re*re+im*im)
}

// "this" can be omitted
operator fun plus(another: Complex): Complex{ // a+b
return Complex(this.re+another.re,this.im+another.im)
}

operator fun minus(another: Complex): Complex{ // a - b
return Complex(re-another.re, im-another.im)
}

operator fun times(another: Complex): Complex{
var resultRe = re*another.re-im*another.im
var resultIm = re*another.im+im*another.re
return Complex(resultRe, resultIm)
}

operator fun compareTo(another: Complex): Int{ // == > < <= >=
if(this.modulus()<another.modulus())
return -1
if(this.modulus()>another.modulus())
return 1
return 0
}

operator fun get(arg: Int): Float{ // someComplex[0]
if(arg==0)
return this.re
if(arg==1)
return this.im
throw IllegalArgumentException()

}

override fun equals(other: Any?): Boolean {
if(other is Complex)
return (this.re==other.re && this.im == other.im)
else
throw Exception("wrong class")
}

override fun toString(): String{
return "($re,$im)"
}

}

fun main() {
val c = Complex(1.0f,0.0f)
val c2 = Complex(1.0f, 0.0f)
val c3 = Complex(10.0f,10.0f)
val sum = c+c2

val sum2 = c.plus(c2)

val someMul = Complex(10.0f,5.0f)* Complex(7.0f,-2.0f)

println("multiplied:$someMul")

println(sum)
println(sum2)
println(sum[0])
println(c3>c2)

println(c==c2)
}

... which defines the class Complex representing a complex number, and overloads the infix "+", "-", "*", comparison (<, >, >=, <=) and [ ] operators. There is also modulus function, which is just a method... And which we use to define our overloaded comparison operators behaviour (operator fun compareTo).

The execution of the code above results in the following output:

multiplied:(80.0,15.0)
(2.0,0.0)
(2.0,0.0)
2.0
true
true

We can see that my implementation of complex multiplication is correct ;)

The +, -, * operators, implemented as plus,  minus, and times are quite self-explanatory: they take one parameter, and use this.re and this.im (or just re and im) of current (left to operand) values to compute the results, which they return. 

In order to override them, we use operator keyword before a method definition. Please note they can be called both as operators ( val sum = c+c2 ) or just as methods ( val sum2 = c.plus(c2)).

In comparison operator overload (operator fun compareTo(another: Complex)) I've used previously defined modulus method, because there is no unequivocal way of comparing complex numbers - so I compare their moduli. This operator should return -1 if the first operand is "smaller" than second one (in our case it's modulus is smaller), 1 if it's "bigger" and and 0 it they're the same.

I've also overloaded get operator, which affects suqare brackets, like in the call println(sum[0]). What it does, is simply return real part of our complex as 0-th element and the imaginary part as 1-st one. If called with something else than 0 or 1, it would throw IllegalArgumentException(). Typically this operator would be used for some class representing collection (like list - please note in Java you'd have to use somelist.get(i) instead of just somelist[i] to get i-th element of the somelist List or like text string, where it would return n-th character).

There are other operators to overload, like not (!) - but I simply can think of no use of it regarding complex numbers :). When you type operator fun  (with the spacebar after the word fun) and hit Ctrl+Space in IntelliJ Idea or Android Studio, you'll get the full list of the operators possible to overload with their symbols on the right and corresponding keywords (like plus or minus in the example above) on the left:



Please note compareTo does not affect == comparison effect: that's what overriden function override fun equals(other: Any?) does.

A word on Java - of course, lack of operator overloading in Java doesn't mean we could not implement a class Complex in this language. We can, of course. But the only way of representing "sum of complex1 and complex2" would be some function or method, like complex1.add(complex2) or maybe sum(complex1, complex2) - not the intuitive and obvious Kotlin's complex1+complex2.

Well, that would be basics. More details, including the full list of operators available for overloading in Kotlin are available in the Kotlin's documentation.


Python crash course part 10: inheritance and polymorphism

In the last part we've shown how to create and use a class in Python. Today we're going to talk about inheritance: wchich means cre...