Python OOP: Four Pillars of OOP in Python 3 for Begineers (Udemy)

04-07-2020

Class and Objects

Noun (name) -->  Class

Adjective (properties) --> Attributes

Verb (action) --> Methods

Methods are functions that are defined within a class. 'self' is a default parameter that the methods accept and is used to access the attributes of the class

In order to access the methods and attributes for the class, we need to create an object for the class

So, although we successfully wrote a class and created two objects, it's clear that we need further improvements to make it more useful. For instance, we'd want to assign different attributes to each object.

Key takeaway: Classes are blueprints and Objects are the execution of that blueprint

Attributes

Class attributes:

These properties remain the same for all objects of the class. 'workplace' below.

Instance attributes:

These properties are specific to each instance of the class. 'name', 'title', 'exptperweek' below.

Methods

Instance methods

These methods use the 'self' parameter to access and modify the instance attributes of your class

Static methods

These methods don't take the default 'self' parameter. These are defined using a decorator called @staticmethod. Decorators take a method and extend the functionality of the method

Say, you were to run the following:

labMember.metgoal(5)
labMember.thankmember()

Since the method in which the instance attribute 'self.name' was defined isn't called, it will throw an 'AttributeError'

However, if you first call the method where the attribute 'self.name' is defined, then it works without this error

So, this makes it clear that we need to have a mechanism that initializes all the attributes of our object of our class before they are to be used. Python has a special method called the init method for this

__init__ method

Abstraction and Encapsulation

Abstraction is the act of only giving out the most essential information while the details are hidden or encapsulated. In other words, abstraction presents a simplified picture of the complexities that are behind the curtain. Encapsulation is the process of hiding the code and data into a single unit to achieve abstraction.

For example, when you apply 'append' to your list, 'append' is the level of abstraction provided to you to add an element to the list. The internals of the 'append' command isn't exposed to the user as it has been encapsulated in the list class.

Inheritance

Inheritance is a way to form new classes using classes that have already been defined. The newly formed classes are called derived classes, and the classes that we derive from are called base classes. Important benefits of inheritance are code reuse and reduction in complexity of a program. The derived classes (descendants) override or extend the functionality of base classes (ancestors).

Single inheritence

Multiple inheritence

The order in which the base classes are defined in the derived class matters. So, if both classes use the same attribute, then the attribute from the first class is used (for instance, 'name' below)

Multilevel inheritence

When you have classes at multiple levels. For instance, let's say there are 3 classes- classA, classB, and classC. Suppose classB inherits from classA, and classC inherits from classB. Then, classC will now have access to all the methods and attributes of both classB and classA.

Access specifiers

Public: public attributes and methods of a class are accessible from within the class, derived classes, as well as from anywhere outside the derived classes

Protected: protected attributes and methods of a class are accessible from within the class and the derived classes

Private: private attributes and methods of a class are accessible only from within the class-- not even the derived classes have access to this information

Unlike other programming languages (C++, Java etc), Python doesn't force access specifiers. However, they are strongly encouraged through a naming convention that has been followed over the years

If we try to access the private attribute of the base class (Vehicle) from the derived class (Car), it'll throw an attribute error:

print(car.__yearOfManufacture)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-23-e98671bca5f4> in <module>()
----> 1 print(car.__yearOfManufacture)

AttributeError: 'Car' object has no attribute '__yearOfManufacture'

Let's create an object tbat belongs to the base class (Vehicle). Now, you'll notice that the public and protected classes can be accessed. However, the private class still throws an AttributeError.

print(vehicle.__yearOfManufacture)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-35-475c338e1691> in <module>()
----> 1 print(vehicle.__yearOfManufacture)

AttributeError: 'Vehicle' object has no attribute '__yearOfManufacture'

The above AttributeError was thrown because Python internally mangles the private data as _Class__PrivateData. So, if you must access the private data (strongly discouraged), you can do so by calling _Class_PrivateData

Polymorphism

Polymorphism is the process of using an operator or function in different ways for different data input. In practical terms, polymorphism means that if class B inherits from class A, it doesn't have to inherit everything about class A. It can override the base class methods and do some of those things differently.

In Python, Polymorphism allows us to define methods in the child class with the same name as defined in their parent class.

As seen above, if the object associated with the base class is used, then the base class method is used. However, if the object associated with the derived class is used, then the method in this derived class is used instead (i.e., base class method overriden).

super() method

If you want to access the overridden method of the base class from the derived class, you can do so using the super() method