Search

Object Oriented Programming (OOP) In Python: Classes and Objects Part 1

Computer scientists are continually refining how they write programs. Therefore, they have resorted to several methodologies to do so. Object Oriented Programming (OOP) is a popular methodology and it is the methodology that python relies on. Other programming languages that rely on the OOP methodology include Java and C++.

oop in python class and object

 

Object oriented programming as the name implies relies on objects as its main concept, and along with other related features of objects like classes, inheritance, abstraction and encapsulation. This is a wide departure from functional programming which depend on functions as its main concept. In OOP, every operation is defined as an object and every attribute is based on that owned by the object.

Object oriented programming has become popular because it brings programming close to real life, to the things people could associate with, and not to mathematical functions that are most times the province of professional scientists and mathematicians.

In python, we will start by describing how python implements classes and objects in OOP before we relate it to other features of OOP in python.

Python Class in OOP.

A class is a blueprint for defining data and behavior of objects. It helps us to define similar objects and relate them to a class. For example if you have two dogs, one called “James” and another called “Bingo”, these dogs are both objects with similar behavior and properties. Therefore, we could create a Dog class for both objects.

When we create a python class, we are bringing together the data and behavior of an object and defining them in a bundle. Therefore, creating a new python class is the same thing as creating a new type of object in python and with the ability that new instances of that type can then be made. For example, if we create a Dog class from the example above, new instances of dogs named ‘James’ and ‘Bingo’ can then be made. These class instances are given the attributes defined in the class to maintain their state, and they can also have methods defined by the class for modifying their state.

It is through the python class definition that we can then implement all the features of object oriented programming in python such as class inheritance allowing for multiple child classes, the overriding of the methods of a parent class by a child class, and a child class having methods that have the same name as the parent class. Note that the amount and kinds of data that objects can contain that are derived from a class is unlimited. Just like modules, classes in python are dynamic in nature since they are created at runtime and can be modified after creation.

The following is the syntax of a python class definition:

    
class ClassName:

    statement 1
    statement 2
    

To define a python class you need to use the keyword class to precede the name of the class. Then the name of the class is followed by a colon. The colon delimits the block of statements that represents what goes into a class like the attributes and methods that the class defines for its objects.

Before a python class definition can have any effect, it must be executed like function definitions. The moment you call a python class, you are creating an object, called class instantiation. You can create and call a class this way:

    
# class definition
class Dog:
    
    def method1():
        statement 1...

# class execution. Creates an object
james = Dog()
        
        

When a class is called, a new namespace is formed which includes all the attributes and methods of the class.

Most times when you are instantiating an object or creating an object, you define the instantiation special method, __init__(). This special method contains all the attributes of the object instances that are needed when objects are created. Therefore, when this exists, the moment you invoke the class by creating an object, the object creation process automatically calls the __init__() special method and implements any statement that are contained within the special method.

For example, let’s take a class Animal that specifies that whenever an Animal object is created, it has to be given a name that would be bound to the object. We could write the code with the __init__() special method this way.

    
# class definition
class Animal:
    
    def __init__(self, name):
        ''' name is a string '''
        self.name = name

# class execution. Creates an object
james = Animal('James')

With the code above any Animal object that is created will be supplied a name. Here we gave the animal the name, James. That name is bound to the python object, james, throughout its lifetime and is unique to it.

Python classes also contain data attributes and methods. The data attributes are of two types: data attributes that are generalized for the class and is applicable to all objects (called class variables) and data attributes that are specific to each instance of a class or each object and called instance variables. I will show how class variables are distinguished from instance variables in another section. But note that the ‘name’ attribute for our Animal class above is an instance variable because it pertains to each specific object of the class.

Python class methods are operations that will be performed by the objects of the class. A method is a function that “belongs to” an object. We add the self parameter when defining methods in a class. This tells the python interpreter that the method belongs to the class that called it. But this need not be enforced although many editors will tell you there was an error if you do not insert the self parameter. Let us illustrate an example of an animal that walks and talks below.

Notice that every method of the class has self as the first parameter. The methods walk and talk define what each object of the animal can do.

Python Objects and OOP.

Objects are the physical entities of the world. They serve as a realization of classes which are the logical entitites. An object can be anything – a book, a student, an animal etc. Objects are defined by three important characteristics: (A). An identity or a piece of information that can be used to identify the object e.g name, number. (B). Properties. The attributes of the object. (C). Behavior. These refers to the operations that are done on the object or the functions it can perform. E.g a student can write, a car can move, an animal can walk.

Objects of a python class support two types of operations: attribute reference and instantiation. I have outlined these two operations in the embedded code above but will bring them out again for emphasis.

In python, the standard syntax for attribute references is the dot syntax. In the above code, when referring to the method walk and talk, we used objectname.walk() and objectname.talk(). Also, in the walk and talk methods when referring to the name data attribute we used self.name. Valid attribute names are all the names that are in the class’s namespace when the python object was created following from the class definition.

Class instantiation that creates a python object uses function notation. Class instantiation results in the creation of an object for the class. We denoted class instantiation above with the code: james = Animal(‘James’) where Animal refers to the class Animal. Animal here is being used as a function to create the object james. The class instantiation function can have an argument or no argument based on how you defined your __init__() method. In our __init__() function above we specified that on object creation we need to supply a name for the object.

Python Class and instance variables in OOP.

As we said above, class variables refer to data and methods that are shared by all instances of the class, while instance variables refer to data and methods that are specific and unique to each instance of a class. If you want an attribute to be a class variable, you should not initialize it in the __init__() method but if you want it to be an instance variable you should initialize it in the __init__() special method. Let’s denote these with examples below.

From the example above you can see that we created two Animal objects, james and frog. In the class definition we defined the type attribute outside the __init__() method and therefore when we call it for both objects we have the same value or reply. But we defined the name attribute inside the __init__() method and then when we called the name attribute we received different values for both objects. Always remember this difference between class variables and instance variables in your code so you don’t get code that doesn’t work as you expect when creating classes and objects.

Data Hiding in Python.

In other object oriented programming languages like java they give you the ability to hide data from users getting access to them from outside the class. In this way, they make data private to the class. The makers of python do not want any data hiding in the language implementation. They state that they want everything in python to be transparent. But there are occasions where you can implement data hiding. This can be achieved by prefixing the attribute with a double underscore, __. When this is done you cannot directly access the attribute outside the class.

For example, let’s make the type attribute hidden in the Animal class.

You can see now that to reference the type attribute we get an AttributeError. But there is a workaround that we can use to get the attribute. When you call objectname._Classname__attributename you can get back that attribute. So nothing is hidden in python. Let’s show this with an example. Take note of line 16 in the code below.

It is beneficial to understand how python implements OOP extensively because when you are working in python, you not only use the built in types but have to create your own types. I hope this post helped you along that line. The subsequent posts will treat on other OOP concepts like python class inheritance in OOP and OOP in python - polymorphism that will build on this knowledge. I hope you also enjoy them.

Happy pythoning.

Sustainable GameBoy That Runs Forever On No-Batteries

Have you ever wished you had a device in which the battery never runs out, or have you ever wanted one day to put an end to all the sustainability issues caused by batteries landing up in landfills? Well, that possibility will soon be a reality with the new proof of concept that was developed by some engineers at Northwestern University and Delft University of Technology (TU Delft) in the Netherlands. They were able to manufacture handheld sustainable game devices that can run without batteries, relying on solar energy and the key presses of the user.

sustainable gameboy

 

Battery-free intermittent computing has long been an idea that has plagued researchers in the technology industry for a long time. With this sustainable device we will soon see an end to the costly and environmentally hazardous batteries that were used to power electronic devices like interactive games which end up in landfills. This device relies on energy from the sun which it attracts and also energy from the user when he presses some keys on the gamepad.

“It’s the first battery-free interactive sustainable device that harvests energy from user actions,” said Northwestern’s Josiah Hester, who co-led the research. “When you press a button, the device converts that energy into something that powers your gaming.”

On September 15, 2020 this team of engineers will present their sustainable game device virtually at the UbiComp 2020 conference.They promise that this is not a toy but the real thing,

So one may ask: how does this device function? This is an energy-aware gaming platform (ENGAGE) that was equipped with precisely the size and form factor of the original Gameboy. The screen has a set of solar panels that attracts and transforms energy from the Sun into its internal energy. Then another source of energy for the device comes from the button presses by the user. It is pertinent to note that an important component of the game device is that it impersonates the original Gameboy processor. While using a lot of computational power, impersonating the processor has the advantage of making it possible that any retro game can be played straight from the original cartridge.

There existed some challenges when the device is power switching. As it switches power from one source to the other the game device can experience loss of power. This problem was overcome when the engineers made the device to be energy aware as well as energy efficient so that the duration of the power failure will become inconsequential. A new technique was also developed to store the system state in non-volatile memory such that the overhead from power failures became minimal and the system could restore itself to previous state when power is restored. This makes it possible that the ‘save’ button which you can find on other devices does not exist as it is state aware and can make the game continue just from precisely where it stopped even if the player was in the course of completing an action.

It was discovered that on days where the sun shone heavily, or the clicking was moderate, interruptions could be ignored by the player. Yet the engineers have not gotten to where they desire the device to be, that is, to have non-interruptible states. But they are happy about one fact - this proof-of-concept shows that sustainable, environmentally-conscious devices that do not use hazardous batteries are possible in the near future.

“Sustainable gaming will become a reality, and we made a major step in that direction — by getting rid of the battery completely,” said TU Delft’s Przemyslaw Pawelczak, who co-led the research with Hester. “With our platform, we want to make a statement that it is possible to make a sustainable gaming system that brings fun and joy to the user.”

“Our work is the antithesis of the Internet of Things, which has many devices with batteries in them,” Hester said. “Those batteries eventually end up in the garbage. If they aren’t fully discharged, they can become hazardous. They are hard to recycle. We want to build devices that are more sustainable and can last for decades.”

You can watch Hester describing this sustainable device in the video below:


 

The 0/1 Knapsack Problem with Dynamic Programming In Python Simplified

This post is related to two previous post. First, the original post on the 0/1 knapsack problem where I explained how this can be solved using a greedy algorithm that doesn’t promise to be optimal and using a brute force algorithm that makes use of the python combinations function. After going through that post, a reader asked me to explore solving the knapsack problem using dynamic programming because the brute force algorithm might not scale to larger number of items. In the other related post which was on dynamic programming, I highlight the main features of dynamic programming using a Fibonacci sequence as example. Now, drawing on these two related posts, I want to show how one can code the knapsack problem with dynamic programming in python that can scale to very large values.

knapsack problem dynamic programming python

 

But first, you have to realize that this problem is good for a dynamic programming approach because it has both overlapping sub-problems and also optimal substructures. That is the reason why memoization would be easily applied to it.

But first, let’s start with analyzing the nature of the problem. If you read the link on the knapsack problem, I said that a burglar has a knapsack with a fixed weight and he is trying to choose what items he can steal from several items such that he would optimize on the weight of the knapsack. Now, to show you how we can do this with dynamic programming in python, I will diverge a little from the example I gave in the former post. Now, I would use a much simpler example in order to show the graphs that would help us do this dynamically.

Let’s say the burglar has 4 items to choose from: a, b, c, and d with values and weights as outlined below.

Name Value Weight
A 6 3
B 7 3
C 8 2
D 9 5

To model the choice process for the burglar, we will use an acyclic binary tree. An acyclic binary tree is a binary tree without cycles and it has a root node with each child node having at most two children nodes. The end nodes of the trees are known as leaf nodes and they have no children. For our binary tree, we will denote each branch of a node as the decision to pick an item or not to pick an item. The root node contains all the items with none picked. Then the levels of the root nodes denote the decision to pick an item, starting from the first ‘a’. The left branch denotes the decision to pick an item and the right branch the decision not to pick an item.

We create each decision node with four sets of features or a quadruple in this order:

1. The set of items that are taken for that level

2. The list of items for which a decision has not yet been made

3. The total value of the items in the set of items taken

4. The available weight or remaining space in the knapsack.

Modeling this in a binary tree we will get a tree looking like this. Each node is numbered.

binary tree python


You can see from the above diagram that each node has the four features outlined above in order. For Node 0, the root, the set of items that are taken are the empty set, the list of items to make a decision is all the items, the total value of items taken is 0, and the remaining space in the knapsack is 5. At the next level, when we choose item ‘a’, the quadruple changes in node 1. We chose the left branch, node 1, because the weight of item ‘a’, 3, is less than the remaining space which is 5. Choosing the left branch, item ‘a’, then reduces the available space to 2, and changes the other items for that node. The right branch to the root node is node 6 which involves the decision not to choose item ‘a’. If we decide not to choose item ‘a’ but remove it from the list, then we see that the set of items taken remain empty, the list of items has reduced to only [b,c,d], the total value of items taken is still 0 and the remaining space is still 5. We do this decision structure, making the decision to choose or not to choose an item and updating the node accordingly until we get to a leaf node.

Characteristics of a leaf node in our graph: a leaf node is a node that either has the list of items not taken to be empty, or the remaining space in knapsack to be 0. Note this because we will reflect this when writing our recursive algorithm.

Now, this problem has overlapping sub-problems and optimal substructures which are features any problem that can be solved with dynamic programming should have. We will refer to the graph above in enumerating these two facts.

Overlapping sub-problems: If you look at each level, you will notice that the list of items to be taken for each level is the same. For example in level 1 for both the left and right branch from the root node, the list of items to be taken is [b,c,d] and each node depends on the list of items to be taken as well as the remaining weight. So, if we can find a solution for the list of items to be taken at each level, and store the result (remember memoization) we could reuse this result anytime we encounter that same list of items in the recursive solution.

Optimal substructure: If you look at the graph, you can see that the solutions to nodes 3 and 4 combine to give the solution for node 2. Likewise, the solutions for nodes 8 and 9 combine to give the solutions for node 7. And each of the sub-problems have their own optimal solutions that would be combined. So, the binary tree representation has optimal substructure.

Noting that this problem has overlapping sub-problems and an optimal substructure, this will help us to now write an algorithm using memoization technique for the knapsack problem. If you want a refresher on the dynamic programming paradigm with memoization, see this blog post which I have earlier referenced.

So, we will take a pass at the code for the technique, using the 4 items outlined above. We will be using a knapsack with a weight of 5kg as the maximal capacity of the knapsack. Run the code below to see how it works. I will explain the relevant parts later.

Now let me explain the relevant parts of the code.

Lines 1 – 20: We created the items class with attributes name, value, and weight. Then a __str__() method so we can print each item that is taken.

Lines 22 – 52: This is the main code that implements the dynamic programming paradigm for the knapsack problem. The name of the function is fast_max_val and it accepts three arguments: to_consider, avail, and a dictionary, memo, which is empty by default. To_consider is the list of items that are yet to be considered or that are pending. Remember, this list is what creates our overlapping sub-problems feature along with the avail variable which refers to the weight remaining in the knapsack. The code uses a recursive technique with memoization implementing this as a decision control structure of if elif else statements. The first if statement checks to see that when we come to a node if the pair of ‘length of items to consider’ and ‘weight remaining’ are in the dictionary. If in the dictionary, memo, this tuple is stated to be the result for that node. If not, we move to the next elif statement, lines 30-31 which checks whether this is a leaf node. If this is a leaf node, then either the list of items to consider is empty or the remaining weight is zero (knapsack full), so we say the node is an empty tuple and assign it to result. Then the next elif statement considers the area of branching. Notice from the graph above that the decision tree involves a left branch (take the next item if weight is within limits) and a right branch (don’t take the next item). This elif statement considers the fact where the left branch is not approached and only the right branch. That means for that level, the right node is the optimal result for our parent node. What we do here is call the fast_max_val function recursively to check for child nodes and store the final result as tuples. Then finally, the last else statement. This considers the case where the two branches can be approached. It recursively calls the child nodes of the branches and stores the final optimal result in the variables with_val and with_to_take for the left branch and without_val and without_to_take for the right branch. Then it evaluates which of these two values has the highest value (remembering we are looking for the highest value in each parent node) and assigns that branch to the result.

After the recursion is complete for each node, it stores the result in our dictionary, before calling for any other recursion. Storing this in dictionary is to ensure that when a similar subproblem is encountered, we already have a solution to that problem, thereby reducing the recursive calls for each node.

Finally, the function returns the result as a tuple of optimal values and list of items that were taken.

Lines 55-66: This lines contains the items builder code. It builds the items and then calls the dynamic programming function. Finally when the function returns, it prints out the items that were taken for the optimal solution to the knapsack problem.

If you would like the code to look at it in-depth and maybe run it on your machine, you can download it here.

Now, the beauty of dynamic programming is that it makes recursion scale to high numbers. I want to show that this is possible with the knapsack problem.

I provide another code that implements a random items generator. The test code uses 250 items generated randomly. You can try it on higher number of random items with random values and weights.

The explanation for the codes is similar to the smaller sample above. If you want to take an in-depth look at the code for the higher number of random items, you can download it here.

Happy pythoning.

Matched content