Kotlin and OOP

Like many JVM languages such as Java, Scala, Groovy, etc, Kotlin supports OOP (Object Orientated Programming). OOP allows developers to create reusable and self-contained software modules known as classes where data and behavior are grouped together and contained within the said class. Such packaging allows developers to think in terms of components when solving a software problem and can improve code reuse and maintainability.

There are often four terminologies that are discussed when explaining OOP. The first term is encapsulation. Encapsulation refers to combining a programs data with the behaviors that operate on the said data. This is different than procedural based programming that treats data and behavior as two seperate concerns. However, encapsulation goes further than just simply grouping behavior and data. It also means that we protect our data inside of the class by only allowing the class itself to use the data. Other users of the class may only work on class data through the class’s public interface.

This takes us into the next concept of OOP, Abstraction. A well designed and encapsulated class functions as a black box. We may use the class, but we may only use it through it’s public interface. The details of how the class works internally are taken away from or Abstracted, from the clients of the class. A car is commonly used as an example of abstraction. We can drive the car using the steering wheel and the foot pedals, but we do not get into the internals of the car and fire the fuel injection at the right time. The car takes care of the details of making it move. We only operate it through its public interface. The details of how a car works are abstracted from us.

OOP promotes code reuse through inheritance. The basic idea is that we can use one class as a template for a more specialized version of a class. For example, we may have a class that represents a Truck. As time went on, we realized that we needed a four wheel drive truck. Rather than writing an entirely new class, we simply create a four wheel drive truck from the truck class. The four wheel drive truck inherits all of the computer code from the truck class, and the developer only needs to focus on code that makes it a four wheel drive truck. Such code reuse not only saves on typing, but it also helps to reduce debugging since developers are free to leverage already tested computer code.

Related to inheritence is polymorphism. Polymorphism is a word that means many-forms. For developers, this means that one object may act as if it were another object. Take the truck example above as an example. Since a four wheel drive truck inherited from truck, the four wheel drive truck may be used whenever the computer code expects a truck. Polymorphism goes a set further in allowing the program to act different depending on the context in which certain portions of computer code are used.

Koltin is a full fleged OOP language (although it does support other programming styles also). The language brings all of the OOP concepts discussed above to the fore-front by allowing us to write classes, abstract their interfaces, extend classes, and even use them in different situations depending on context. Let’s begin by looking at a very basic example of how to write and create a class in Kotlin.

package ch1

class Circle(
        //Define data that gets associated with the class
        private val xPos : Int = 20,
        private val yPos : Int = 20,
        private val radius : Int = 10){

    //Define behavior that uses the data
    override fun toString() : String =
            "center = ($xPos, $yPos) and radius = $radius"
}

fun main(args: Array<String>){
    val c = Circle() //Create a new circle
    val d = Circle(10, 10, 20)
    
    println( c.toString() ) //Call the toString() function on c
    println( d.toString() ) //Call the toString() function on d
}

In the above program, we have a very basic example of a Kotlin class called Circle. The code inside of lines 3-12 tell the Kotlin compiler how to construct objects of Type Circle. The circle has three properties (data): xPos, yPos, and radius. It also has a function that uses the data: toString().

In the bottom half of the program, the main method creates two new circle objects (c and d). The circle c has the default values of 20, 20, and 10 for xPos, yPos, and radius because we used the no parenthesis constructor (). Lines 5-7 in the circle class tell the program to simply use 20, 20, and 10 as default values in this case. Circle d has different valeus for xPos, yPos, and radius because we supplied 10, 10, 20 to the constructor. Thus we have an example of polymorphism in this program because two different constructors were used depending on the program’s context.

When we print on lines 18 and 19, we get two different outputs. When we call c.toString(), we get the String “center = (20, 20) and radius = 10” printed to the console. Calling toString() on d results in “center = (10, 10) and radius = 20”. This works because both c and d are distinct objects in memory and each have there own values for xPos, yPos, and radius. The toString() function acts on each distinct object, and thus, the output of toString() reflects the state of each Circle object.

Advertisements

Kotlin Koans—Part 11

This portion of the Kotlin Koans tutorial focuses on Object Expressions. Practically speaking, Object Expressions serve the same role as anonymous innner classes in Java. They let us make modifications on a class in one particular case without having to create an entirely new class.

This portion of the tutorial has developers creating a dynamic Comparator class that sorts numbers in descending order.

fun task10(): List {
    val arrayList = arrayListOf(1, 5, 2)
    Collections.sort(arrayList, object: Comparator {
        override fun compare(o1: Int?, o2: Int?): Int {
            return o2?.compareTo(o1 ?: 0) ?: 0
        }
    })
    return arrayList
}

We could have used a lambda in this case, but that would miss the point of what the tutorial is trying to teach. In this code snippet, the second paramter of Collections.sort is an Object Expression that defines a custom Comparator class.

You’ll notice that the definition of compare is full of null safe expressions as indicated the by ? and ?: opeartors. As a side note, I really like how Kotlin has an arrayListOf() function that let’s you create an ArrayList. Sure it does the same thing as Arrays.asList, but again, it’s more concise.

You can view part 10 here

Kotlin Koans—Part 9

Java and Kotlin are strongly typed languages. It’s not necessary to cast types when working up an object graph. For example

public void sort(Collection col){
    //todo
}

sort(new ArrayList());
sort(new HashSet());

This is an example of polymorphism in Java. ArrayList and HashSet are both Collections so it’s acceptable to pass either types to the example sort method.

Keep in mind this is not a two way street. This code would not compile.

public void sort(List list){
    //todo
}

Collection col = new ArrayList();
sort(col); //Compile error!
sort((List) col); //OK

Even though col points at an ArrayList and ArrayList implements List, Java forbids you to pass col to sort without a cast. This is because the compiler has no idea that col is pointing at an ArrayList. Keep in mind this is true of Kotlin also.

Although we can get our code to compile with a cast, it’s still dangerous code. Let’s tweak it a little big and have col point at a HashSet instead of ArrayList.

public void sort(List list){
    //todo
}

Collection col = new HashSet();

//Compiles but throws
//ClassCastException
sort((List) col);

Now the code compiles, but it will fail at run time. There is no way to cast HashSet to a List. HashSet does not implement List in anyway so when the code attempts to make the cast, the code will fail. We have to use the instanceof operator to make sure the cast is safe first.

public void sort(List list){
    //todo
}

Collection col = new HashSet();

if (col instanceof List){
    //Now it's safe
    sort((List) col);
}

This code is now safe. It will check if the runtime type of col is a List first. If the object is a List, it will make the cast. Otherwise, the cast will not get made.

Tutorial

This portion of the Kotlin Koans tutorial shows off how Kotlin handles casting compared to Java. Here is the Java code that needs to get rewrote in Kotlin.

public class JavaCode8 extends JavaCode {
    public int eval(Expr expr) {
        if (expr instanceof Num) {
            return ((Num) expr).getValue();
        }
        if (expr instanceof Sum) {
            Sum sum = (Sum) expr;
            return eval(sum.getLeft()) + eval(sum.getRight());
        }
        throw new IllegalArgumentException("Unknown expression");
    }
}

Kotlin has a when keyword that is used for casting. Here is the equivalent Kotlin code.

fun todoTask8(expr: Expr): Int {
    when (expr) {
        is Num -> return expr.value
        is Sum -> return todoTask8(expr.left) + todoTask8(expr.right)
        else -> throw IllegalArgumentException("Unknown expression")
    }
}

As usual, Kotlin is more concise than Java. The when block starts with the when followed by the variable in question. You can have any number of is clauses in this statement followed by the type. The variable is automatically cast to the specified type on the right hand side of the -> operator.

You can click here to see Part 8

Kotlin Koans—Part 7

This portion of the Kotlin Koans showed off a really boss feature of the language: Data Classes. These are special classes whose primary purpose is to hold data.

Here is a Java class that we start with that’s taken directly from the tutorial.

public class Person {
        private final String name;
        private final int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }
    }

It’s not a special class. This is a class with a primary constructor, getters/setters and two private variables. It’s also one of my biggest complaints about the Java language. I can do the same thing in Python like this.

class Person:
    def __init__(self, name=None, age=None):
        self.name = name
        self.age = age

Four lines of code in Python. In all fairness to Java, would could just declare name and age to be public variables, but doing so is not only frowned upon, but many Java libraries look for getter/setter method to access a property of a Java bean. Basically speaking, even though we could allow for public access of a Java property, it’s not really practical at this point.

There is a Java library called Lombok that does a lot to solve this problem.

@Data
public class Person {
    private String name;
    private String age;
}

Lombok has been the solution I have used for most of my projects. It’s not perfect however. For example, I can’t use the @Data annotation to make a read only class. That forces me to use a mix of Lombok annotations or define a stereotype annotation. It’s not a huge problem, but it’s still something to think about.

Kotlin data classes take this to a whole other level. Here is the same class in Kotlin.

data class Person(val name: String, val age: Int)

That’s it! One line of code!!! With this single line of code, we get our two properties, it’s getter methods, hashcode, equals, toString() and a constructor. The class is immutable because the variables are declared with the val keyword. We can make the class mutable by using var instead of val. Finally, we aren’t losing our ability to work with existing Java libaries.

I really don’t see how it can get any better than this. I have used data classes countless times when working with ORM libraries such as hibernate. I’d say 95% of these classes are classes that just hold data and map to a table in the database. Although any IDE can generate constructors, getters/setters, equals, and hashcode, and toString, let’s face it, it’s even better to have this built directly into the language itself.

You can click here to see Part 6

Lists—Python

Lists are a sequence type object that Python provides to us for grouping data together into a single variable. Let’s consider a common application of a list before we go into details.

names = ['Bob Belcher',
         'Linda Belcher',
         'Tina Belcher',
         'Gene Belcher',
         'Louise Belcher']
for name in names:
    print (name)

This code creates a list called names and populates it with five names. Then it prints the names to the console. We could accomplish the same output by using this code.

bob = 'Bob Belcher'
linda = 'Linda Belcher'
tina = 'Tina Belcher'
gene = 'Gene Belcher'
louise = 'Louise Belcher'

print(bob)
print(linda)
print(tina)
print(gene)
print(louise)

A quick comparison shows that the first example is not only much easier to read, but it is also more maintainable. If we want to print additional names, we only need to add them to the names list in the first example. However, in the second example, we need to add new name variable and another print statement. This may not seem like a big deal with six names, but obviously 6,000 is a much different case.

It’s for this reason that almost every programming language provides some sort of a collection object. List is one such data type that Python provides out of the box. Let’s look at common list operations.

Check if item is in the list

We may want to check if a list has a certain item.

names = ['Bob Belcher',
         'Linda Belcher',
         'Tina Belcher',
         'Gene Belcher',
         'Louise Belcher']
if 'Bob Belcher' in names:
    print('Found Bob')

This code prints ‘Found Bob’ because 'Bob Belcher' in names returns True.

Check if item is not in list

We can also check if a list does not have an item.

names = ['Bob Belcher',
         'Linda Belcher',
         'Tina Belcher',
         'Gene Belcher',
         'Louise Belcher']
if 'Teddy' not in names:
    print('No Teddy here!')

This code would print ‘No Teddy here!’ because 'Teddy' not in names is True. Our names list does not have ‘Teddy’

Combine lists

We can add two lists together (called concatenation) using the + operator.

belchers = ['Bob Belcher',
            'Linda Belcher',
            'Tina Belcher',
            'Gene Belcher',
            'Louise Belcher']

pestos = ['Jimmy Pesto',
          'Jimmy Pesto Jr.',
          'Andy Pesto',
          'Ollie Pesto']

family_frackus = belchers + pestos
print(family_frackus)

This code will print all of the names to standard out when run.

Accessing items

Lists use the index operator access items

belchers = ['Bob Belcher',
            'Linda Belcher',
            'Tina Belcher',
            'Gene Belcher',
            'Louise Belcher']
print(belchers[0])
print(belchers[3])

Keep in mind that lists are 0 based, so belchers[0] is ‘Bob Belcher’ while belchers[3] is ‘Gene Belcher’

Add item to list

We can add an item to a list using the append() method.

belchers = ['Bob Belcher',
            'Linda Belcher',
            'Tina Belcher',
            'Gene Belcher',
            'Louise Belcher']
belchers.append('Teddy')
print(belchers[5])

Remove item from a list

We use the del operator to remove an item from a list

belchers = ['Bob Belcher',
            'Linda Belcher',
            'Tina Belcher',
            'Gene Belcher',
            'Louise Belcher']
del belchers[0]

Using del belchers[0] removes ‘Bob Belcher’ from the list.

Replace item in a list

We can replace an item in a list by specifying the index and assigning a new value to it.

belchers = ['Bob Belcher',
            'Linda Belcher',
            'Tina Belcher',
            'Gene Belcher',
            'Louise Belcher']
belchers[0] = 'Mort'

The code belchers[0] = 'Mort' replaces ‘Bob Belcher’ with ‘Mort’

Length of the list

We get the length of the list using len.

belchers = ['Bob Belcher',
            'Linda Belcher',
            'Tina Belcher',
            'Gene Belcher',
            'Louise Belcher']
print(str(len(belchers))) 

The print(str(len(belchers))) prints 5 to the console.

Conclusion

We can do a lot more with lists than what was discussed in this post. Make sure you check out the Python documentation for a complete list of features!

Circular Linked List—Python

The circular linked list a variant of the singly linked list where the last Node links to the head rather than None. Since the list contains a circular reference to the Head, we can navigate a circular linked list starting at any index rather than starting at the head node.

Here is the code for a circular linked list implementation with unit tests. We won’t dicuss the unit tests in this post, but we will go over the linked list implementation.

from enum import Enum


class NodeConstants(Enum):
    FRONT_NODE = 1


class Node:
    def __init__(self, element=None, next_node=None):
        self.element = element
        self.next_node = next_node

    def __str__(self):
        if self.element:
            return self.element.__str__()
        else:
            return 'Empty Node'

    def __repr__(self):
        return self.__str__()


class CircularLinkedList:
    def __init__(self):
        self.head = Node(element=NodeConstants.FRONT_NODE)

        self.head.next_node = self.head

    def size(self):
        count = 0
        current = self.head.next_node

        while current != self.head:
            count += 1
            current = current.next_node

        return count

    def insert_front(self, data):
        node = Node(element=data, next_node=self.head.next_node)
        self.head.next_node = node

    def insert_last(self, data):
        current_node = self.head.next_node

        while current_node.next_node != self.head:
            current_node = current_node.next_node

        node = Node(element=data, next_node=current_node.next_node)
        current_node.next_node = node

    def insert(self, data, position):
        if position == 0:
            self.insert_front(data)
        elif position == self.size():
            self.insert_last(data)
        else:
            if 0 < position < self.size():
                current_node = self.head.next_node
                current_pos = 0

                while current_pos < position - 1:
                    current_pos += 1
                    current_node = current_node.next_node

                node = Node(data, current_node.next_node)
                current_node.next_node = node
            else:
                raise IndexError

    def remove_first(self):
        self.head.next_node = self.head.next_node.next_node

    def remove_last(self):
        current_node = self.head.next_node

        while current_node.next_node.next_node != self.head:
            current_node = current_node.next_node

        current_node.next_node = self.head

    def remove(self, position):
        if position == 0:
            self.remove_first()
        elif position == self.size():
            self.remove_last()
        else:
            if 0 < position < self.size():
                current_node = self.head.next_node
                current_pos = 0

                while current_pos < position - 1:
                    current_node = current_node.next_node
                    current_pos += 1

                current_node.next_node = current_node.next_node.next_node
            else:
                raise IndexError

    def fetch(self, position):
        if 0 <= position < self.size():
            current_node = self.head.next_node
            current_pos = 0

            while current_pos < position:
                current_node = current_node.next_node
                current_pos += 1

            return current_node.element
        else:
            raise IndexError


import unittest
from random import randint


class TestCircularLinkedList(unittest.TestCase):
    names = ['Bob Belcher',
             'Linda Belcher',
             'Tina Belcher',
             'Gene Belcher',
             'Louise Belcher']

    def test_init(self):
        dll = CircularLinkedList()
        self.assertIsNotNone(dll.head)
        self.assertEqual(dll.size(), 0)

    def test_insert_front(self):
        dll = CircularLinkedList()
        for name in TestCircularLinkedList.names:
            dll.insert_front(name)

        self.assertEqual(dll.fetch(0), TestCircularLinkedList.names[4])
        self.assertEqual(dll.fetch(1), TestCircularLinkedList.names[3])
        self.assertEqual(dll.fetch(2), TestCircularLinkedList.names[2])
        self.assertEqual(dll.fetch(3), TestCircularLinkedList.names[1])
        self.assertEqual(dll.fetch(4), TestCircularLinkedList.names[0])

    def test_insert_last(self):
        dll = CircularLinkedList()
        for name in TestCircularLinkedList.names:
            dll.insert_last(name)

        for i in range(len(TestCircularLinkedList.names) - 1):
            self.assertEqual(dll.fetch(i), TestCircularLinkedList.names[i])

    def test_insert(self):
        dll = CircularLinkedList()
        for name in TestCircularLinkedList.names:
            dll.insert_last(name)

        pos = randint(0, len(TestCircularLinkedList.names) - 1)

        dll.insert('Teddy', pos)
        self.assertEqual(dll.fetch(pos), 'Teddy')

    def test_remove_first(self):
        dll = CircularLinkedList()
        for name in TestCircularLinkedList.names:
            dll.insert_last(name)

        for i in range(dll.size(), 0, -1):
            self.assertEqual(dll.size(), i)
            dll.remove_first()

    def test_remove_last(self):
        dll = CircularLinkedList()
        for name in TestCircularLinkedList.names:
            dll.insert_last(name)

        for i in range(dll.size(), 0, -1):
            self.assertEqual(dll.size(), i)
            dll.remove_last()

    def test_remove(self):
        dll = CircularLinkedList()
        for name in TestCircularLinkedList.names:
            dll.insert_last(name)

        dll.remove(1)

        self.assertEqual(dll.fetch(0), 'Bob Belcher')
        self.assertEqual(dll.fetch(1), 'Tina Belcher')
        self.assertEqual(dll.fetch(2), 'Gene Belcher')
        self.assertEqual(dll.fetch(3), 'Louise Belcher')


if __name__ == '__main__':
    unittest.main()

NodeContants

NodeConstants is an example of Python’s enumeration. A circular linked list requires a distinct head node that the client code can easily identify. Without a distinct head node, we could easily introduce an infinate loop when traversing the linked list. We are going to use NodeContants to help identify the head node.

from enum import Enum


class NodeConstants(Enum):
    FRONT_NODE = 1

There are other ways to indentify the head node, so using enumerations isn’t required. It does give us a way to show off how to do enumerations in Python for those readers who are interested.

Node

We can use the same Node class that we used in singular linked list. Like all linked lists, the Node class holds the data stored in the list and a reference to the next Node in the list.

class Node:
    def __init__(self, element=None, next_node=None):
        self.element = element
        self.next_node = next_node

    def __str__(self):
        if self.element:
            return self.element.__str__()
        else:
            return 'Empty Node'

    def __repr__(self):
        return self.__str__()

CircularLinkedList

This class is the work house of this module and provides us with the linked list implementation. It’s not very different than the singular linked list implementation.

class CircularLinkedList:
    def __init__(self):
        self.head = Node(element=NodeConstants.FRONT_NODE)
        self.head.next_node = self.head

    def size(self):
        count = 0
        current = self.head.next_node

        while current != self.head:
            count += 1
            current = current.next_node

        return count

    def insert_front(self, data):
        node = Node(element=data, next_node=self.head.next_node)
        self.head.next_node = node

    def insert_last(self, data):
        current_node = self.head.next_node

        while current_node.next_node != self.head:
            current_node = current_node.next_node

        node = Node(element=data, next_node=current_node.next_node)
        current_node.next_node = node

    def insert(self, data, position):
        if position == 0:
            self.insert_front(data)
        elif position == self.size():
            self.insert_last(data)
        else:
            if 0 < position < self.size():
                current_node = self.head.next_node
                current_pos = 0

                while current_pos < position - 1:
                    current_pos += 1
                    current_node = current_node.next_node

                node = Node(data, current_node.next_node)
                current_node.next_node = node
            else:
                raise IndexError

    def remove_first(self):
        self.head.next_node = self.head.next_node.next_node

    def remove_last(self):
        current_node = self.head.next_node

        while current_node.next_node.next_node != self.head:
            current_node = current_node.next_node

        current_node.next_node = self.head

    def remove(self, position):
        if position == 0:
            self.remove_first()
        elif position == self.size():
            self.remove_last()
        else:
            if 0 < position < self.size():
                current_node = self.head.next_node
                current_pos = 0

                while current_pos < position - 1:
                    current_node = current_node.next_node
                    current_pos += 1

                current_node.next_node = current_node.next_node.next_node
            else:
                raise IndexError

    def fetch(self, position):
        if 0 <= position < self.size():
            current_node = self.head.next_node
            current_pos = 0

            while current_pos < position:
                current_node = current_node.next_node
                current_pos += 1

            return current_node.element
        else:
            raise IndexError

__init__

We initialize the linked list by creating a head Node and then pointing it’s next_node at itself.

def __init__(self):
    self.head = Node(element=NodeConstants.FRONT_NODE)
    self.head.next_node = self.head

In this case, we will use our NodeConstants.FRONT_NODE to help us indentify the head of the list in the debugger. We don’t actually need this but it does help make the code more clear.

size

This method returns the number of elements contained in the linked list.

def size(self):
    count = 0
    current = self.head.next_node

    while current != self.head:
        count += 1
        current = current.next_node

    return count

We begin by making a count variable and a current variable. Current points at self.head.next_node because we aren’t counting self.head. Now we are going to loop until current == self.head. We don’t need to check for None in this case because we don’t have any such Nodes in this implementation.

As we loop, we increment count by one and then advance current to the next node in the list. Eventually, current points at self.head and we terminate the loop at this point. We then return the count.

insert_front

There isn’t much work to do to insert a Node at the beginning of the list.

def insert_front(self, data):
    node = Node(element=data, next_node=self.head.next_node)
    self.head.next_node = node

We create a new Node and point it’s next node at self.head.next_node. Then we just need to point self.head.next_node at the new Node.

insert_last

To insert a Node at the end of the list, we need to tranverse the list to right before self.head.

def insert_last(self, data):
    current_node = self.head.next_node

    while current_node.next_node != self.head:
        current_node = current_node.next_node

    node = Node(element=data, next_node=current_node.next_node)
    current_node.next_node = node

Once again, we have a current_node that requires us to start at self.head.next_node. We then enter a loop that terminates when current_node.next_node == self.head to avoid an infinate loop.

Once we find our insertion point, we create a new Node and point it’s next_node to current_node.next_node (which happens to be self.head). Then current_node.next_node is updated to point at Node.

insert

The insert method let’s us support insertions in the middle of the list. It works by traversing the list to right before the desired position and performing an insertion.
Keep in mind this method has four possible scenerios it must take into account.

  1. Position is 0 -> insert at the front
  2. Position == size() -> insert the end
  3. Position size() -> throw exception
  4. Position > 0 and Position Perform insertion
def insert(self, data, position):
    if position == 0:
        # Case 1
        self.insert_front(data)
    elif position == self.size():
        # Case 2
        self.insert_last(data)
    else:
        if 0 < position < self.size():
            # Case 4
            current_node = self.head.next_node
            current_pos = 0

            while current_pos < position - 1:
                current_pos += 1
                current_node = current_node.next_node

            node = Node(data, current_node.next_node)
            current_node.next_node = node
        else:
            # Case 3
            raise IndexError

The cases have been identified with the comments. In cases one and two, we are simply going to reuse code by calling self.insert_front or self.insert_last respectively. We handle case three by raising IndexError to indicate a programming error.

Case four works similar to other other insertions. We start with current_node at self.head.next_node and current_pos at 0. Then we iterate through the list until we reach the node right before the specified position (position – 1).

After exiting the while loop, we create a new Node and point it's next_node at current_node.next_node. The we update current_node.next_node to point at our new Node which now resides at our position.

remove_first

When removing nodes from the front of the list, we reassign self.head.next_node rather than self.head.

def remove_first(self):
    self.head.next_node = self.head.next_node.next_node

Remember that the last Node in this linked list always points at self.head. If we accidently reassigned self.head rather than self.head.next_node, we would break our linked list. However, when we update self.head.next_node to point at self.head.next_node.next_node, we are removing the Node currently located at self.head.next_node.

The removed Node gets garbage collected by the Python runtime environment and the linked list is shrunk by one element.

remove_last

It’s a fairly painless process to remove elements from the end of a circular linked list. We simply need to advance to the element located two positions before self.head and then point that Node’s next_node at self.head.

def remove_last(self):
    current_node = self.head.next_node

    while current_node.next_node.next_node != self.head:
        current_node = current_node.next_node

    current_node.next_node = self.head

We begin with current_node pointing at self.head.next_node and then enter a while loop. Notice that the condition on the while loop is current_node_next_node.next_node != self.head. We want to advance to the second to last element in this list.

Once we have positioned current_node to the proper index in the list, we remove the last node by pointing current_node.next_node at self.head. The removed Node ends up getting grabage collected by Python’s runtime.

remove

The remove method supports removing items from the middle of the list. It has to account for the same cases as insert.

def remove(self, position):
    if position == 0:
        # Case 1
        self.remove_first()
    elif position == self.size():
        # Case 2
        self.remove_last()
    else:
        if 0 < position < self.size():
            # Case 3
            current_node = self.head.next_node
            current_pos = 0

            while current_pos < position - 1:
                current_node = current_node.next_node
                current_pos += 1

            current_node.next_node = current_node.next_node.next_node
        else:
            # Case 4
            raise IndexError

Once again, we are going to dicuss case 3. We start with current_node pointing at self.head.next_node and current_pos = 0. We traverse the list until we arrive at the Node located before position. Now we nust point current_node.next_node at current_node.next_node.next_node. The removed Node gets garbage collected by the Python runtime.

fetch

This method let’s us get data out of the list.

def fetch(self, position):
    if 0 <= position < self.size():
        current_node = self.head.next_node
        current_pos = 0

        while current_pos < position:
            current_node = current_node.next_node
            current_pos += 1

        return current_node.element
    else:
        raise IndexError

After checking position to make sure it's valid, we traverse the list until we arrive at the position. Then we return current_node.element. If position isn't valid, we raise an exception.

Conclusion

This code shows an example a circular linked list, but it’s a simple implementation that we could optimize. This implementation always starts at self.head and traverse the list to a required position, but it could operate by tracking the most recently accessed Node and starting traversals from that point rather than always starting at the front of the list.

Stack—Python

Implementing a Stack is a common problem that computer science students are asked to do while learning about data structures. Stacks are a data structure that implemenet the LIFO (last in, first out) principle. We can implement stacks using a linked list methodology or an array backed list.

In the real world, Python’s built in list object is one of many production tested objects that can and should be used for stacks. Our stack is a linked list implementation of a simple stack.

Here is the example code that we will work with followed by an explanation about how it works.

import unittest

class Node:
    def __init__(self, element=None, next_node=None):
        self.element = element
        self.next_node = next_node

    def __str__(self):
        if self.element:
            return self.element.__str__()
        else:
            return 'Empty Node'

    def __repr__(self):
        return self.__str__()

class EmptyStackException(Exception):
    pass

class Stack:
    def __init__(self):
        self.top = None
        self.size = 0

    def push(self, data):
        self.top = Node(data, self.top)
        self.size += 1

    def pop(self):
        if self.top:
            val = self.top.element
            self.top = self.top.next_node
            self.size -= 1
            return val
        else:
            raise EmptyStackException

    def peek(self):
        if self.top:
            return self.top.element
        else:
            raise EmptyStackException

    def empty(self):
        return self.size == 0

    def __str__(self):
        elements = []
        curr = self.top
        while curr is not None:
            elements.append(curr.element)
            curr = curr.next_node
        return elements.__str__()

    def __repr__(self):
        return self.__str__()

class TestStack(unittest.TestCase):
    def __init__(self, method_name='runTest'):
        super().__init__(method_name)
        self.names = ['Bob Belcher',
                      'Linda Belcher',
                      'Tina Belcher',
                      'Gene Belcher',
                      'Louise Belcher']

    def test_init(self):
        stack = Stack()
        self.assertIsNone(stack.top)
        self.assertEqual(stack.size, 0)

    def test_push(self):
        stack = Stack()
        for name in self.names:
            stack.push(name)

        names = list(self.names)
        names.reverse()

        self.assertEqual(names.__str__(), stack.__str__())
        self.assertEqual(len(names), stack.size)

    def test_pop(self):
        stack = Stack()
        for name in self.names:
            stack.push(name)

        self.assertEqual(stack.pop(), 'Louise Belcher')
        self.assertEqual(stack.size, 4)

        self.assertEqual(stack.pop(), 'Gene Belcher')
        self.assertEqual(stack.size, 3)

        self.assertEqual(stack.pop(), 'Tina Belcher')
        self.assertEqual(stack.size, 2)

        self.assertEqual(stack.pop(), 'Linda Belcher')
        self.assertEqual(stack.size, 1)

        self.assertEqual(stack.pop(), 'Bob Belcher')
        self.assertEqual(stack.size, 0)

        with self.assertRaises(EmptyStackException):
            stack.pop()

    def test_peek(self):
        stack = Stack()
        for name in self.names:
            stack.push(name)

        self.assertEqual(stack.peek(), 'Louise Belcher')
        self.assertEqual(stack.size, 5)

        stack = Stack()
        with self.assertRaises(EmptyStackException):
            stack.peek()

    def test_empty(self):
        stack = Stack()
        self.assertTrue(stack.empty())

        for name in self.names:
            stack.push(name)

        self.assertFalse(stack.empty())

if __name__ == '__main__':
    unittest.main()

Node

Since this is a linked list implementation of a stack, we are going to begin with the Node class. The Node does the work of holding the data in the stack and contains a reference to the next item in the stack.

class Node:
    def __init__(self, element=None, next_node=None):
        self.element = element
        self.next_node = next_node

    def __str__(self):
        if self.element:
            return self.element.__str__()
        else:
            return 'Empty Node'

    def __repr__(self):
        return self.__str__()

There isn’t a lot going on in this class in terms of methods. It has an __init__ method which takes optional element and next_node parameters that default to None. This class also implements __str__ and __repr__ so that debuggers such as PyCharm print out user readable information.

EmptyStackException

The EmptyStackException class is a custom exception that gets raised when client code attempts to remove items from an empty stack.

class EmptyStackException(Exception):
    pass

This class doesn’t do anything other than subclass the Exception base class. For this reason, it’s implemented with a pass statement.

Stack

Stack is our main class the implements a Stack. We begin with the code for this class followed by an explanation about how it works.

class Stack:
    def __init__(self):
        self.top = None
        self.size = 0

    def push(self, data):
        self.top = Node(data, self.top)
        self.size += 1

    def pop(self):
        if self.top:
            val = self.top.element
            self.top = self.top.next_node
            self.size -= 1
            return val
        else:
            raise EmptyStackException

    def peek(self):
        if self.top:
            return self.top.element
        else:
            raise EmptyStackException

    def empty(self):
        return self.size == 0

    def __str__(self):
        elements = []
        curr = self.top
        while curr is not None:
            elements.append(curr.element)
            curr = curr.next_node
        return elements.__str__()

    def __repr__(self):
        return self.__str__()

__init__

The __init__ method intializes the data structure to an empty stack.

def __init__(self):
    self.top = None
    self.size = 0

We have two class variables in our stack. The first variable, self.top, is a Node that represents the last item inserted into the Stack. It’s next_node property points to the next item in the stack, and so one. The other class variable is self.size, which represents the number of items that are placed in the stack.

push

The push method is used to add items to the stack.

def push(self, data):
    self.top = Node(data, self.top)
    self.size += 1

Whenever we add items to a Stack, the newest Node becomes the top of the stack. If you remember back from our Node class, it’s __init__ method has an optional next_node argument we can use to initialize the new Node with it’s next_node. This works well for us here because we can pass self.top as the next_node of the new Node and then immediatly assign the new Node to self.top. In this way, the last item in the Stack get’s shifted right and the new item becomes the top of the Stack. The next line of code simply increments the size of the stack by one, thus completing the operation of adding (or pushing) an item onto the Stack.

pop

The pop method is opposite operation to the push method. This method removes an item from the Stack.

def pop(self):
    if self.top:
        val = self.top.element
        self.top = self.top.next_node
        self.size -= 1
        return val
    else:
        raise EmptyStackException

The pop method needs to consider two possible cases. The fist case involves a Stack that has items on it and needs an item removed. The other case involves an empty stack. Let’s begin with the first case.

We start by checking if the stack has items. If the stack is empty, self.top is None. Assuming that self.top is not None, we begin by storing the data contained in self.top.element in a variable called val. Our next job is to delete that Node from the Stack by changing self.top to point at self.top.next_node. The original Node will get garbage collected by the Python runtime. Now that we have removed the Node, we need to shrink the size of the stack by one. After we decrease self.size, we can return val to the caller thus completing the pop operation.

If it turns our that the stack is empty, we need to notify the caller. I think it would be a very bad practice to simply return None, since calling pop() on an empty Stack would indicate a programming error. Thus, we raise EmptyStackException. The client code will either need to handle the Exception or allow the program to crash.

peek

The peek operation lets client code take a look at the first element in the Stack without removing the item.

def peek(self):
    if self.top:
        return self.top.element
    else:
        raise EmptyStackException

In this case, we just just need to return self.top.element if the stack has an item, or raise an EmptyStackException if the stack is empty.

empty

Clients of this class need to check if the Stack is empty prior to calling pop() or peek()

def empty(self):
    return self.size == 0

This method simply returns True if self.size == 0 or False if self.size is non-zero.

__str__ and __repr__

We don’t need these methods to represent a Stack, but if you choose to implement them, debuggers such as the on found in PyCharm will use these methods to print more user friendly data to the debugger window.

def __str__(self):
    elements = []
    curr = self.top
    while curr is not None:
        elements.append(curr.element)
        curr = curr.next_node
    return elements.__str__()

def __repr__(self):
    return self.__str__()

We are going to leverage Python’s list object to get a reader friendly String representation of our Stack. We start by making an empty list called elements. Then we create a curr variable that points at self.stop. Now, we are going to loop until the end of the Stack by checking if curr is None.

During each iteration of the while loop, we add curr.element to elements. Then we advance to the next node by assigning curr = curr.next_node. When we are done, we will return elements.__str__() which creates a String for us. The __repr__ method works by calling self.__str__().

Conclusion

This article is a simple linked list implementation of a Stack. Stacks are useful in cases where you need a LIFO data structure to track the order of which items are added to the stack. As you can see, a Stack is a much simplier data structure to implement than a linked list.