Python object-oriented programming (OOP)

python Object Oriented Programming

Object-oriented programming, usually known as OOP, is a concept that’s been floating around for at least two decades. Its name refers to the fact that it implies a model similar to real life objects. These objects, just like in the real world, have certain characteristics or attributes that make them special

Examples of object-oriented programming

your desk is an object. But we have a huge number of different desks in the world. They are not created equal because they differ in design, size, color, type of material, features, and other things. The only common aspect is the fact that they’re all desks.

Or think about cars instead. We all know what a car looks like, but they aren’t all identical. They’re defined by various attributes like brand, type, model, year, color, fuel type, horsepower, engine type, and so on. In addition to attributes, they are also characterized by a number of “methods” that are shared, such as start, go, turn left, turn right, break, and stop. All of these actions are taken by all cars no matter their attributes.

 In the two examples, the chairs and the cars are classes. The class creates them all in all their forms and variations. Once the class builds a car, it becomes its own independent object. If we change this particular car’s attributes, the class itself will not be affected. Other cars won’t be affected either.

Most important terms you need to know

Class:

They are blocks of code used to create an object, which becomes an instance of that class. Imagine our schematic scenarios discussed above. 

Instance:

A section of data created from the class. This is what’s also known as an object, where each instance is a different object spawned by the class. 

Attributes:

Objects, or instances of a class, have certain characteristics that describe them. In programming, attributes are sometimes also called properties.

Methods:

Classes often have functions attached to them, which define what actions they can perform. For instance, a car class would have a “go” method, which is attached to the name of the class (or object) with the use of a dot. Like so: car. go. 

The four Python coding styles

Imperative

You only use statements to create and manipulate data. The program starts with the first line of code and ends with the last line. 

Every beginner starts with imperative. This paradigm is the best way to introduce programming to people. Imperative programing is also easier to read and digest. However, it easily loses its readability when the lines of codes increase by the hundreds. 

Structured 

Dealing with conditional statements, loops, and code blocks transitioned you to structural or structured programming. You learn to group statements into blocks that aim to perform a single goal and introduce logical branches in your program through controlling the flow of the program using conditional and loop statements. 

Procedural 

When you learned about functions, you were introduced to procedural programming. Procedural programming is a subset of imperative programming combined with structured programming.

At this point, you group your assignment, conditional, and loop statements into functions. You also group your functions into modules, which allows you to take advantage of modular programming. 

You use functions to repeatedly and conditionally perform series of statements to achieve certain goals in your program. 

Object Oriented 

If you recall, I mentioned earlier that every element in Python is an object. A number is an object. A list is an object. And even a function is an object. However, using objects does not automatically make you a programmer that performs OOP.

 In object oriented programming, you perform imperative, structured, and procedural. The biggest exception is the usage of classes and objects. 

What exactly are classes and objects anyway?

 Objects are elements in a program that contains attributes, properties, and methods. Attributes are actually variables of an object and methods are functions of an object. Properties, on the other hand, are attributes with programmer-defined setter, getter, and delete methods.

Classes, on the other hand, are an object’s template. When you define a class, you code the attributes and methods of any object that is created using the class as its template. 

For Example

>>> x = [1, 2, 3, 4] 
>>> id(x) 
4408744 
>>> x.extend([5]) 
>>> x 
[1, 2, 3, 4, 5] 
>>> _ 

In this example, [1, 2, 3, 4] is an attribute of the object 4408744. It is a variable with a value that can be obtained and assigned with another value. Extend, on the other hand, is a method that do certain tasks in the object. And list is object 4408744’s class.

How to create a class in python

Classes are created the same as functions. You can name them however you want as long as the name begins with a letter and contains no punctuation marks or spaces. However, when it comes to classes, there’s an unwritten rule generally agreed upon by programmers around the world. The name of a class should start with an uppercase letter. Yes, you can use lowercase letters as well, but by using an uppercase letter we are able to quickly see the difference between a class and any other variable. You’ll read a lot of code, and following such rules and conventions will make it much easier. So, to begin, you need to type the keyword “class”, followed by the name you give it and a colon. Here it is in code:

>>> class student:
    def __init__(self, name, gender, age, sid): 
    self.name = name 
    self.gender = gender 
    self.age = age 
    self.sid = sid 
    def details(self): 
    print("Name: " + self.name) 
    print("ID: " + self.sid) 
    print("Age: " + self.age)
    print("Gender: " + self.gender) 
>>> _

The example created a class named student. The code has done three things: create attributes, defined a method, and set the initial parameters.

The attributes created were name, gender, age, and sid. The method created was details(). 

By the way, the __init__ or initializer function is a special function that gets executed when you create an object from a class for the first time. All objects in Python have this method. Furthermore, note you have two underscore before and after init.

Making an Instance from a Class and Instantiating a Child Object

The next example will show how to create a method using the student class.

 >>> student1 = student("Johnny", "Male", “18”, 
“1000121”)
 >>> student1.details() 
     Name: Johnny
     ID: 1000121 
     Age: 18 
     Gender: Male
 >>> _ 

The example created an object by using the class name and providing arguments to the parameters listed in the initializer method. By the way, the “self” identifier is not a keyword. It is a convention that you can use to refer to the object or the object that called the method. More about this will be explained later.

 In the case of the init method, the self refers to the object itself. Also, since the value of self is already established as the object, you do not need to give it an argument. Just proceed with the other parameters.

Objects created from a class are called instances.

Python  Object Oriented Programming Working With Classes and Instances

Classes and instances are handy to use when your program requires objects that contain similar number and kinds of variables and use similar functions.

 Most Content Management Systems used on websites (e.g., WordPress) make use of classes and instances. One of the classes that WordPress has is wp_post. Instances of this class often contain a single blog or page post in a WordPress website. 

Some of the attributes that the wp_post has are id, post_author, post_title, post_date, and post_content. The wp_post class is written inside the wp-includes/class-wp-post.php module. By the way, WordPress is written in PHP.

Python  Object Oriented Programming Writing Parent and Child Classes

If you are going to write a basic version of wp_post class in Python, it might look like this:

 >>> class wp_post:
     post_id = "" 
     post_author = "" 
     post_title = "" 
     post_content = "" 
     def __init__(self): 
     print("Instance of wp_post class created.")
     def displayAttributes(self): 
     print("Title: " + self.post_title)
     print("Author: " + self.post_author)
     print("ID: " + self.post_id) 
     print("Content: " + self.post_content) 
>>> _ 

For the sake of the next examples, this example will supposedly be saved in the directory as class_wp_post.py. 

To assign a value to an attribute, you can simply access it from the instance using the accessor operator (.) and assign a value to it. For example:

>>> from class_wp_post import wp_post 
>>> post1 = wp_post() Instance of wp_post class created. 
>>> post1.post_id = 1
>>> post1.post_title = "This is a new website!" 
>>> post1.post_author = "It’s Me, Mario"
>>> post1.post_date = "January 1, 2010" 
>>> post1.post_content = "Hello World!"
>>> post1.displayAttributes() 
Title: This is a new website! 
Author: It’s Me, Mario 
ID: 1 
Content: Hello World! 
>>> _ 

Python Attribute Value

You can set default values for the attribute by simply assigning them a value when they are declared outside methods. For example:

 >>> class wp_post: 
     post_id = "1" 
     post_author = "None" 
     post_title = "Untitled"
     post_content = "No Content" 
>>> post1 = wp_post() 
>>> post1.post_id 
'1' 
>>> _ 

Python  Object Oriented Programming Modifying Attribute Values

 You can change the value of an attribute or attributes by accessing them from the instance using the accessor operator (.) and assigning them the value that you want. For example:

 >>> class wp_post:
     post_id = "1" 
     post_author = "None" 
     post_title = "Untitled" 
     post_content = "No Content"

 >>> post1 = wp_post()
 >>> post1.post_id = 10 
>>> post1.post_id 
10 
>>> _ 

Python  Object Oriented Programming Inheritance

Inheritance is the ability of a class to inherit or copy another class’ attributes and methods. When inheritance is involved in making classes, classes can be divided into two: parent and child. 

Parent (also called super, base, and ancestor) classes are where child classes inherit or copies attributes and properties from. Child, also called sub, derived and descendant, classes copy attributes and methods from parent classes.

However, be aware that child classes do not completely copy all methods. To be precise, they do not copy constructors (__init__()) and destructors (__del__()). 

Defining Attributes and Methods for the Child Class 

Inheritance is useful if you work with multiple classes with common attributes and methods. For example:

>>> class staff(): 
    name = "" 
    gender = "" 
    age = "" 
    employee_id = "" 
    def setDetails(self, name, gender, age, employee_id): 
    self.name = name 
    self.gender = gender 
    self.age = age 
    self.employee_id = employee_id 

def getDetails(self): 
   print("Name: "  + self.name) 
   print("Gender: " + self.gender) 
   print("Age: " + self.age) 
   print("ID: " + self.employee_id)
 >>> class supervisor(staff): 
team_members = [] 

>>> class cashier(staff): 
team_supervisor = "" 

>>> class waiter(staff): 
team_supervisor = ""
>>> _

 For convenience, this example is saved as a module with the file name class_staff.py and will be used in other examples that need it. 

In this example, all instances created from the supervisor, cashier, and waiter classes will have all the attributes and methods of the staff class. For example: 

>>> import class_staff 
>>> s1 = class-staff.supervisor()
>>> s1.getDetails() 
Name: 
Gender: 
Age: 
ID: 
>>> s1.setDetails("John Doe", "Male", 25, 100001) 
>>> s1.getDetails() 
Name: John Doe 
Gender: Male 
Age: 25 
ID: 100001 
>>> _ 

As you can see, despite being an instance of the supervisor class, the instance was able to use the methods written for the staff class. 

Using Pass in Python

There are times that you do not have any attributes or methods to assign a child class. Unfortunately, Python’s syntax does not allow an empty indented block or a statement with no indentation after a class and function declaration. For example:

>>> class parent(): 
... 
File "<stdin>", line 2
 ^ 
IndentationError: expected an indented block 
>>> _ 

#To avoid getting this error, you can use the pass keyword. 

>>> class parent():
 pass 
>>> class child(parent):
 pass 
>>> _ 

#Defining a purposeless variable would work, too. For example:

 >>> class parent(): 
uselessVariable = "" 
>>> _ 

But this method is inefficient and confusing.

The init() Method For Child Class

It has been said that inheritance does not include constructors and destructors. This means that you should set the constructors and destructors method for each class that you make. However, what if you want to use the __init__() method of your parent class for your child’s classes? It can be easily done by calling the __init__() method of the parent class. Here is a modified version of the previous example:

>>> class staff(): 
name = ""
gender = "" 
age = "" 
employee_id = "" 
def __init__(self, name, gender, age, employee_id):

    self.name = name
    self.gender = gender 
    self.age = age 
    self.employee_id = employee_id 

def getDetails(self): 
    print(self.name) 
    print(self.gender) 
    print(self.age) 
    print(self.employee_id) 

class supervisor(staff): 
    def __init__(self, name, gender, age, employee_id):

 super().__init__(name, gender, age, employee_id) 
team_members = []
 class cashier(staff):
 def __init__(self, name, gender, age, employee_id): 
super().__init__(name, gender, age, employee_id) 
team_supervisor = "" 
class waiter(staff): 
def __init__(self, name, gender, age, employee_id):
 super().__init__(name, gender, age, employee_id) 
team_supervisor = "" 
>>> _

In here, each child class modified its own __init__() method. In the __init__() method, there is only one statement. The statement accesses and calls the parent class’ __init__() method and passes the parameter values as arguments to the parent’s __init__() method’s parameters. It can be confusing at first since it might appear that you are just writing parameters instead of actually assigning arguments. 

You might notice that there are three things missing in the statement. First, the parent class’ object is missing. Second, the super() function that was placed. Third, the “self” argument was not passed as an argument. The super() function acts as a replacement and an implicit method to access the parent class. Also, using the super() built-in automatically passes the “self” argument to the __init__() method. 

Aside from that, super is a cleaner and efficient way to handle child classes with multiple parent classes. Yes, a child class can have multiple parents just like parent classes can have multiple child classes. 

Overriding methods from the parent class in Python

There will be times when you want a certain child class’s method to behave differently than the method written in the parent class. Also, there will be times that you want to add a few more actions to the parent’s method for a certain child class. To get through those scenarios efficiently and easily, you can override the parent’s methods inside the child’s class. For example:

 >>> class parent():
     def someFunction(): 
     print("This is the original function")
 >>> class child1(parent): 
     pass 
>>> class child2(parent): 
def someFunction():
 print("This is an overridden function") 

>>> x = child1()
>>> y = child2() 
>>> x.someFunction() 
This is the original function >>> 
y.someFunction() 
This is an overridden function
>>> _ 

In the example, there are three classes: parent(), child1(parent), and child2(parent). The parent class has a method called someFunction(). Since the child1() only passes, it will inherit the method someFunction(). On the other hand, child(2) has defined a method someFunction() similar to the parent’s someFunction(), but with different statements. 

When you call the sameFunction() method in an instance created from the child2() class, it will use the sameFunction() code written in child2() instead of the one written in parent() despite being a parent of the child2() class. 

How about if you want to add some statements to the parent’s method for one of the child classes? You can do it by defining the method on the child class. Then call the parent’s method, and write the statements that you want to add.

Here is how you do it:

>>> class parent(): 
def someFunction(self): 
print("This is the original function") 

>>> class child1(parent): 
    pass 
>>> class child2(parent): 
    def someFunction(self):
    super().someFunction() 
    print("This is additional code") 
>>> x = child1() 
>>> y = child2() 
>>> x.someFunction() 
This is the original function 
>>> y.someFunction() 
This is the original function This is additional code 
>>> _ 

This is similar to how you override the __init__() method in the previous sections. By the way, always remember to add the “self” parameter to methods that you want to override when you use the super() function. 

The super() function always send a reference to the object that calls the method. And if you use super() on a parent method without parameters, it would return an error. For example:

>>> class parent(): 
def someFunction(): 
print("Something.") 
>>> class child1(parent):
def someFunction(): 
super().someFunction()
 print("Something Something") 
>>> x = child1() 
>>> x.someFunction() 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
TypeError: someFunction() takes 0 positional arguments but 1 was given

 Importing Classes in python

To import classes, you can treat classes as if they are functions from a module. Of course, save the classes on a module first. By the way, some of the previous examples took advantage of importing classes if you have noticed. 

For example: 

>>> class staff(): 
    name = "" 
    gender = ""
    age = "" 
    employee_id = "" 
def setDetails(self, name, gender, age, employee_id): 
self.name = name 
self.gender = gender 
self.age = age 
self.employee_id = employee_id 
def getDetails(self): 
print("Name: "  + self.name) 
print("Gender: " + self.gender) 
print("Age: " + self.age) 
print("ID: " + self.employee_id)

>>> class supervisor(staff):
 team_members = [] 

>>> class cashier(staff): 
team_supervisor = "" 

>>> class waiter(staff): ... 
team_supervisor = "" 
>>> _ 

Save this to class_staff.py. Then import it just like a module. 


>>> import class_staff 
>>> x = class_staff.cashier() 
>>> x.getDetails()
 Name: 
 Gender: 
 Age:
 ID:
 >>> _ 

If you do not want to access the module name every time you use the class, you can always use the from and import combination. You can load the child’s class alone if you want. Python usually imports the code for the parent if it is on the same module. 

However, that is a practice that may form a habit, which is a bad one. There are times when the codes for the parent and child classes are saved in different modules. When you only import the child class in that situation, it might result in an error.

For example, say that the code for the staff() class is saved on module1.py and the supervisor(staff) class is saved on module2.py. 

>>> from module2 import supervisor 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module>
 File "<C:\Python\module2.py", line 1, in <module> 
class supervisor(staff): 
NameError: name 'staff' is not defined 
>>> _ 

To solve this, you must make sure that you import the parent classes first even if they are in the same module to prevent any error from popping up.

>>> from module1 import staff 
>>> from module2 import supervisor
>>> x = supervisor() 
>>> x.getDetails()
Name: 
Gender: 
Age: 
ID: 
>>> _

By the way, note that you should name modules as if you are creating an identifier in Python. This means that letters, numbers, and underscores are the only characters that you are allowed to use in the module’s file name. Here is what will happen if you do not follow this: 

>>> import class-staff 
File "<stdin>", line 1 Import class-staff
SyntaxError: invalid syntax
 >>> _ 

Name Mangling in Python

Name mangling is a naming technique in Python used to create “private” and “hidden” attributes. As you already know, variables used in functions become local variables. These local variables are inaccessible outside the scope of the function. However, variables that become attributes in classes are always accessible by using the accessor operator (.).

 In some cases, you might want some of the attributes in your classes inaccessible outside but still accessible within the class. This is done through name mangling. 

To mangle an attribute, all you need to do is to place two trailing underscores before a variable. For example: 

>>> class someClass(): 
variable1 = 0 
__variable2 = 0 
>>> x = someClass()
>>> x.variable1 0 
>>> x.__variable2 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
AttributeError: 'someClass' object has no attribute '__variable2' 
>>> _

You may be wondering, where did __variable2 go? You see, when you mangle an attribute in Python, its identifier changes to this format: _<class name><attribute>. So, variable __variable2 will become _someClass__variable2. 

>>> class someClass(): 
variable1 = 0
 __variable2 = 0 

>>> x = someClass() 
>>> x.variable1 
0 
>>> x._someClass__variable2 
0 
>>> dir(x)
 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_someClass__variable2', 'variable1'] 
>>> _ 

What is self in Python?

You have already been exposed to the “self” argument in the past few sections. But, what is it exactly? Self is a variable by convention that can be used as an argument to contain a reference to the object using it. By convention, it means that usage of the “self” variable is something that programmers formed as a habit and practice. Python does not

force anyone to use the “self” identifier or variable in method definitions. 

Nonetheless, we can double-check if it does really contain an object by using this simple code. 


>>> class sample(): 
def check(self): 
return self 
>>> x = sample() 
>>> y = sample() 
>>> x == x.check() 
True 
>>> x == y
False 
>>> x == x 
True 
>>> id(x) 
4319280 
>>> id(x.check()) 
4319280 
>>> _ 

In the above example, we have created the class sample(). The class contains a method named check() that contains the parameter and argument self. The method returns the value of check when called. 

Then, the example created two instances of the sample() class: x and y. The example then checked if object x is equal to the “self” argument that the method check() will return. The comparison returns True. 

To double check, objects x and y were compared, and it returns False. Then the ids of object x and the “self” argument were checked. Both had the same id. 

The “self” variable is handy to use when referencing to the object that calls the method. After all, the method declaration cannot exactly include the possible names of the instances created using the class.

 Since self is just an identifier, it is not a reserved keyword in Python. This means that you can actually use it as a variable, function, or class and assign something to it. It is encouraged to primarily use it for the purpose of readability, and it is highly discouraged to use it aside from the purpose of referencing the calling objects themselves.

 And since it is only a convention, you can actually use other variables or parameters to catch the object reference. For example: 

>>> class sample():
 def check(catcher): 
return catcher 
>>> x = sample()
 >>> x == x.check() 
True 
>>> id(x) 
4319280 
>>> id(x.check()) 
4319280 
>>> _ 

Class and Static Methods in Python

Methods in classes can be categorized into two: class and static methods. By default, all methods are automatically set as class methods. 

The main difference between the two is that class methods automatically pass the calling object as an argument by reference while static methods do not. This is the reason it is required to have a self or any parameter that will receive the reference in class methods. Without a parameter to catch the reference to the calling object, Python will return an error. For example:  

>>> def sampleClass(): 
def sampleFunction(): 
print("Nothing") 

>>> x = sampleClass()
 >>> x.sampleFunction() 
Traceback (most recent call last): 

File "<stdin>", line 1, in <module> 
TypeError: sampleFunction() takes 0 positional arguments but 1 was given 
>>> _ 

As you can see, Python detected that the class method call automatically sent a positional argument in form of the self object reference. And since the method sampleFunction() did not have any parameter to catch the argument, Python returned a Traceback TypeError. 

Static methods in python

Static methods, on the other hand, do not pass any argument by default, so you can create methods that do not contain parameters.

In order to define class and static methods you need to use the decorator operator (@). For example: 

>>> class classA(): 
    @classmethod 
    def methodA(): 
    print("This is a Class Method.") 

@staticmethod 
def methodB(): 
print("This is a Static Method.")   

 >>> class classB(classA): 
   Pass
>>> x = classB() 
>>> x.methodB() 
This is a Static Method.
 >>> x.methodA() 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
TypeError: sampleFunction() takes 0 positional arguments but 1 was given 
>>> _ 

Styling classes in python

  • Always use “cls” as the first parameter for class methods. 
  • For instance methods, always use cls for the first argument.
  • Method naming rules and conventions are similar to function and variable naming.
  •  Put a leading underscore in methods and variables you wish not to make public. They can still be seen during runtime, but the leading underscore will discourage other people to do something with methods and variables with it. 
  • Use name mangling if the methods and attributes you have in the superclass will clash with the methods and attributes of a sub-class. 
  • Of course, people have different views on name mangling. If it works for you, use it. You do not need to follow the style recommendations to the T.
  •  When it comes to naming classes it is advisable to use the CapWords convention. 
  • You can use the naming convention for functions, methods, and variables if the class is primarily used to be called. 
  • The usage of CapWords on class names makes them completely distinguishable from built-in names. The only objects that use the CapWords convention are built-in constants and exceptions.

1 thought on “Python object-oriented programming (OOP)

Comments are closed.