OPP in Python

Ref: https://www.geeksforgeeks.org/python-oops-concepts/

OPP Concepts in Python

  • Class: a blueprint for creating objects
  • Object: an instance of a class
  • Attribute: a variable that belongs to a class or object
  • Method: a function that belongs to a class or object
  • Inheritance: a class can inherit attributes and methods from another class
  • Encapsulation: restricting access to some components of the class
  • Polymorphism: a concept where an object can take on many forms, i.e., objects can share the same method name but those methods can act differently based on the object (overriding)
  • Abstraction: hiding the complexity of the class and only showing the necessary features of the class
  • Class variable: a variable that is shared among all instances of a class
  • Instance variable: a variable that is unique to each instance of a class
  • Special instance methods: __str__, __repr__, __eq__
  • Multiple inheritance: a class can inherit from multiple classes
class Person:
    # class variable or attribute
    number_of_people = 0

    def __init__(self, name, age):
        self.name = name # instance variable or attribute
        self.age = age

    def __str__(self):
        return f"{self.name} is {self.age} years old."

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

Initialize two objects of the class Person:

p1 = Person("A", 32)
p2 = Person("A", 32)
p1 == p2 # False

even though p1 and p2 have the same name and age, they are different objects in memory.

Special instance methods

  • __str__: return a string representation of the object
  • __repr__: return a string representation of the object that can be used to recreate the object
  • __eq__: compare two objects
p1 = Person("A", 32)
print(p1) # A is 32 years old.

Inheritance

class Student(Person):
    def __init__(self, name, age, student_id):
        super().__init__(name, age)
        self.student_id = student_id

    def __str__(self):
        return f"{self.name} is {self.age} years old and has student ID: {self.student_id}."

class MonashStudent(Student):
    pass # inherit all methods and attributes from Student

class UniMelbStudent(Student):
    pass # inherit all methods and attributes from Student

class RMITStudent(Student):
    def __init__(self, name, age, student_id, rmit_id):
        super().__init__(name, age, student_id) # inherit all methods and attributes from Student. Equivalent to super(RMITStudent, self).__init__(name, age, student_id)
        # Student.__init__(self, name, age, student_id) # another way to call the parent class's method
        self.rmit_id = rmit_id # add new attribute
    
    def __str__(self):
        # override the __str__ method of the parent class
        return f"{self.name} is {self.age} years old and has student ID: {self.student_id} and RMIT ID: {self.rmit_id}."

m1 = MonashStudent("A", 32, 123)
print(m1) # A is 32 years old and has student ID: 123.
m2 = UniMelbStudent("B", 33, 124)
print(m2) # B is 33 years old and has student ID: 124.

super() is used to call the parent class’s methods. In case of complicated class hierarchy, super() is used to call the parent class’s methods without explicitly mentioning the parent class. If super(RMITStudent, self).__init__(name, age, student_id) is used, it is equivalent to super().__init__(name, age, student_id). However, it is recommended to use super() without arguments.

Multiple Inheritance

class A:
    def __init__(self):
        print("A")
    
    def method(self):
        print("A method")
    
class B:
    def __init__(self):
        print("B")
    
    def method(self):
        print("B method")

class C(A, B):
    def __init__(self):
        super().__init__()
        print("C")
    
    def method(self):
        super().method()
        print("C method")

c = C()
c.method()

In the above example, C inherits from both A and B. When C is initialized, A is printed first, then C. When method is called, A method is printed first, then C method.

Python Encapsulation

  • Public: accessible from outside the class
  • Protected: accessible only to the class and subclasses
  • Private: accessible only to the class. Private attributes are prefixed with double underscores __
class Car:
    def __init__(self, name, model, price):
        self.name = name # public attribute
        self._model = model # protected attribute
        self.__price = price # private attribute

    def get_price(self):
        return self.__price

c = Car("Toyota", "Camry", 20000)
print(c.name) # Toyota
print(c._model) # Camry
print(c.__price) # AttributeError: 'Car' object has no attribute '__price'
print(c.get_price()) # 20000
print(c._Car__price) # 20000, tricky way to access private attribute

Private methods are accessible outside their class, just not easily accessible. Nothing in Python is truly private; internally, the names of private methods and attributes are mangled and unmangled on the fly to make them seem inaccessible by their given names (source: Real Python).

Python Polymorphism

class Dog:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return self.name + " says woof!"

class Cat:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return self.name + " says meow!"

def get_pet_sound(pet):
    print(pet.speak())

d = Dog("Dog")
c = Cat("Cat")
get_pet_sound(d) # Dog says woof!
get_pet_sound(c) # Cat says meow!

Python Abstraction

It hides unnecessary code details from the user. Also, when we do not want to give out sensitive parts of our code implementation and this is where data abstraction came.

Data Abstraction in Python can be achieved by creating abstract classes.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius * self.radius

    def perimeter(self):
        return 2 * 3.14 * self.radius

c = Circle(5)
print(c.area()) # 78.5
print(c.perimeter()) # 31.4

In the above example, Shape is an abstract class with two abstract methods area and perimeter. Circle is a subclass of Shape and it implements the area and perimeter methods. The Shape class is an abstract class because it has abstract methods. An abstract class cannot be instantiated. For example, s = Shape() will raise an error.

Multithreading in Python

https://www.geeksforgeeks.org/multithreading-python-set-1/

MongoDB - PyMongo

https://www.w3schools.com/python/python_mongodb_create_db.asp

Records in a MongoDB database are called documents, and the field values may include numbers, strings, booleans, arrays, or even nested documents.

{
	title: "Post Title 1",
	body: "Body of post.",
	category: "News",
	likes: 1,
	tags: ["news", "events"],
	date: Date()
}

SQL vs Document Databases

SQL databases are considered relational databases. They store related data in separate tables. When data is needed, it is queried from multiple tables to join the data back together.

MongoDB is a document database which is often referred to as a non-relational database. This does not mean that relational data cannot be stored in document databases. It means that relational data is stored differently. A better way to refer to it is as a non-tabular database.

MongoDB stores data in flexible documents. Instead of having multiple tables you can simply keep all of your related data together. This makes reading your data very fast.

You can still have multiple groups of data too. In MongoDB, instead of tables these are called collections.

PyMongo

PyMongo is a Python distribution containing tools for working with MongoDB, and is the recommended way to work with MongoDB from Python.

Some important functions in PyMongo:

Create a database:

import pymongo

myclient = pymongo.MongoClient("mongodb://localhost:27017/")

mydb = myclient["mydatabase"]

Check if a database exists:

print(myclient.list_database_names())

Create a collection:

mycol = mydb["customers"]

Check if a collection exists:

print(mydb.list_collection_names())

Insert a document:

mydict = { "name": "John", "address": "Highway 37" }
x = mycol.insert_one(mydict)
print(x.inserted_id)

The insert_one() method returns a InsertOneResult object, which has a property, inserted_id, that holds the id of the inserted document.

Insert multiple documents with specified IDs:

import pymongo

myclient = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = myclient["mydatabase"]
mycol = mydb["customers"]

mylist = [
  { "_id": 1, "name": "John", "address": "Highway 37"},
  { "_id": 2, "name": "Peter", "address": "Lowstreet 27"},
  { "_id": 3, "name": "Amy", "address": "Apple st 652"},
  { "_id": 4, "name": "Hannah", "address": "Mountain 21"},
  { "_id": 5, "name": "Michael", "address": "Valley 345"},
  { "_id": 6, "name": "Sandy", "address": "Ocean blvd 2"},
  { "_id": 7, "name": "Betty", "address": "Green Grass 1"},
  { "_id": 8, "name": "Richard", "address": "Sky st 331"},
  { "_id": 9, "name": "Susan", "address": "One way 98"},
  { "_id": 10, "name": "Vicky", "address": "Yellow Garden 2"},
  { "_id": 11, "name": "Ben", "address": "Park Lane 38"},
  { "_id": 12, "name": "William", "address": "Central st 954"},
  { "_id": 13, "name": "Chuck", "address": "Main Road 989"},
  { "_id": 14, "name": "Viola", "address": "Sideway 1633"}
]

x = mycol.insert_many(mylist)

#print list of the _id values of the inserted documents:
print(x.inserted_ids)

Gradio

Event Listerners

Description

Event listeners allow you to respond to user interactions with the UI components you’ve defined in a Gradio Blocks app. When a user interacts with an element, such as changing a slider value or uploading an image, a function is called.

Two important events are upload and click

To re-upload a file, click to symbol x at the corner

Get the value of an element

To get the value of an element, use the value attribute of the element. For example, to get the value of a slider, use slider.value.

Example

import gradio as gr

def greet(name):
    return "Hello " + name + "!"

def upload_file(file):
    return file.name

def click_button():
    return "Button clicked!"

iface = gr.Interface(
    fn=greet,
    inputs="text",
    outputs="text",
    title="Greet",
    description="Greet someone by their name."
)

iface.launch()

HTML

Basic

  • HTML stands for Hyper Text Markup Language
  • HTML and CSS are not programming languages. They are markup languages.
  • HTML is used to define the structure of a web page.
  • CSS is used to style a web page.

Basic Structure

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    Hello World
</body>
</html>

HTML Tags

  • <html>: The root element of an HTML document.
  • <head>: The head element contains meta-information about the HTML document.
  • <title>: The title of the HTML document.
  • <body>: The body of the HTML document.
  • <h1> to <h6>: Headings of different levels.
  • <p>: Paragraph.
  • <a>: Anchor, used for links.

In HTML, there are two types of tags:

  • Block-level elements: These elements start on a new line and take up the full width available to them. Examples include <h1>, <p>, <div>, and <form>.
  • Inline elements: These elements do not start on a new line and only take up as much width as necessary. Examples include <span>, <a>, <img>, and <input>.

HTML Attributes

HTML attributes provide additional information about HTML elements. They are always specified in the start tag and usually come in pairs like name="value".

Define a new HTML element

<tagname attribute="value">Content goes here...</tagname>

And style it with CSS:

<style>
    tagname {
        property: value;
    }
</style>

We can also define a new class in HTML:

<div class="myclass">Content goes here...</div>

And style it with CSS:

<style>
    .myclass {
        property: value;
    }
</style>

Java Script

Basic