3  Object oriented programming

3.1 Basic building elements of OOP

  1. Inheritance provides code reusability. We have single, multi-level, multiple (more than one base class) and hierarchical (when more than one derived class are created from a single base)
Code
# egz. of inheritance and use of super() function

class Fruit():
    def __init__(self, name):
        print(name)

class Apple(Fruit):
    def __init__(self, name, color):
        self.color = color
        super().__init__(name) 
  1. Polymorphism means the ability to take multiple forms. So if the parent class has a method named ABC then the child class also can have a method with the same name ABC having its own parameters and variables.
  2. Encapsulation is a process of wrapping data and functions that perform actions on the data into a single entity. A single unit is referred to as a class. To access the values, the class usually provides publicly accessible methods (setters and getters). Technique that hides implementation details.
  3. Abstraction is used to hide something too, but in a higher degree (class, interface). Clients who use an abstract class do not care about what it was, they just need to know what it can do.

3.2 Methods, classes

  • init is a method that is automatically called to allocate memory when a new object (i.e. instance of a class) is created. It acts as a constructor which gets executed when a new object is instantiated and allows the class to classify its attributes.
  • self is an object of a class. The self variable in the init method refers to the newly created object, while in other methods it refers to the object whose method was called. It is used to refer to the object properties of a class.
  • object() returns featureless object that is a base for all classes
Note

As Python has no concept of private variables, leading underscores are used to indicate variables that must not be accessed from outside the class.

  • Named Tuple can be a great alternative to construct a class. It is an extension of the Python built-in tuple data type, which is structure for grouping objects with different types. When you access an attribute of the built-in tuple, you need to know its index. Named Tuple allows us to give names to the elements, so we can access the attributes by both attribute name and its index. It is good practice to use classes constructed like this when we have a function that takes more than 3 arguments, which is too much. Then it is better to pack most of the arguments into a class.
Code
from typing import NamedTuple

class Transaction(NamedTuple):
    sender: str
    receiver: str
    date: str
  • Class attributes belong to every instance of some class. They are defined outside of init function. So they are different from instance attributes.
  • Decorator is a design pattern that allows a user to add new functionality to an existing object without modifying its structure. They are usually called before the definition of a function you want to decorate. Decorator takes in a function and returns it by adding some functionality. A few good examples for using decorators are when you want to add logging, test performance, perform caching, verify permissions…
Code
def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner

@make_pretty
def ordinary():
    print("I am ordinary")

# ordinary()
# Output: 
#   I got decorated
#   I am ordinary