Python

Python is a high-level, interpreted programming language known for its simplicity and readability.

Python Syntax

  • Indentation: Python uses indentation to define blocks of code. Indentation is typically done using four spaces, and it's crucial for readability and proper code structure. 
  • Comments: Comments in Python start with the # symbol and are used to annotate code for better understanding. Comments are ignored by the interpreter and are for human readers.  
  • Statements: Python statements are typically written on separate lines. However, you can use a semicolon ; to write multiple statements on a single line.  
  • End-of-Line Termination: Unlike some languages, Python does not use semicolons ; to terminate statements. Instead, it uses line breaks.

Variables

Variable

  • Variables are used to store data values in Python. Variable names can contain letters, numbers, and underscores, but they cannot start with a number. Here's how you define and use variables:

# Variable assignment
x = 10
name = "John"

# Variable reassignment
x = 20

# Multiple assignments
a, b, c = 1, 2, 3

# Constants (convention)
PI = 3.14 

Data Types

  • Python supports various data types, including:

Integers:
Integers are whole numbers without any decimal point.

x = 10
y = -5 

Floats:
Floats represent real numbers and include a decimal point.

pi = 3.14
gravity = 9.8 

Strings:
Strings represent sequences of characters and are enclosed in single or double quotes.

You can perform various operations on strings, such as concatenation, slicing, and formatting.

name = "John"
message = 'Hello, world!'

# Concatenation
full_name = name + " Doe"  

# Slicing
first_name = full_name[:4]  # Output: John  

# Formatting
formatted_message = f"Hello, {name}!"  # Output: Hello, John! 

Lists:
Lists are ordered collections of items, and they can contain elements of different data types. Lists are mutable, meaning they can be modified after creation.

You can perform various operations on lists, such as appending elements, accessing elements by index, and slicing.

my_list = [1, 2, 3, "a", "b", "c"]

 # Appending elements
 my_list.append(4)  # Adds 4 to the end of the list  

# Accessing elements by index
 first_element = my_list[0]  # Output: 1  

# Slicing
 subset = my_list[1:4]  # Output: [2, 3, 'a'] 

Tuples:
Tuples are ordered collections of items, similar to lists, but they are immutable, meaning they cannot be modified after creation.

You can perform operations like accessing elements by index and slicing, but you cannot modify the tuple once it's created.

my_tuple = (1, 2, 3, "a", "b", "c")
 
# Accessing elements by index
 first_element = my_tuple[0] # Output: 1

# Slicing
subset = my_tuple[1:4] # Output: (2, 3, 'a')

Dictionaries:
Dictionaries are unordered collections of key-value pairs, where each key is associated with a value. Dictionaries are mutable and can contain elements of different data types.

You can access values using keys, add new key-value pairs, and remove key-value pairs from dictionaries.

my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}

 # Accessing values using keys
 person_name = my_dict['name']  # Output: John  

# Adding new key-value pairs
 my_dict['occupation'] = 'Engineer'  

# Removing key-value pairs
 del my_dict['city'] 

Basic Operators

  • Python supports various operators for performing operations on data. Here are some common ones:

Arithmetic Operators:
Arithmetic operators are used to perform mathematical operations.

# Addition
 result_addition = 10 + 5  # Output: 15  

# Subtraction
 result_subtraction = 10 - 5  # Output: 5  

# Multiplication
 result_multiplication = 10 * 5  # Output: 50  

# Division
 result_division = 10 / 5  # Output: 2.0 (always returns a float)  

# Floor
 Division result_floor_division = 10 // 3  # Output: 3 (returns integer quotient)  

# Modulo
 result_modulo = 10 % 3  # Output: 1 (returns remainder)  

# Exponentiation
 result_exponentiation = 2 ** 3  # Output: 8 (2 raised to the power of 3) 

Comparison Operators:
Comparison operators are used to compare values.

# Equal to
 is_equal = (10 == 5)  # Output: False  

# Not equal to
 not_equal = (10 != 5)  # Output: True  

# Greater than
 greater_than = (10 > 5)  # Output: True  

# Less than
 less_than = (10 < 5)  # Output: False  

# Greater than or equal to
 greater_than_equal = (10 >= 5)  # Output: True  

# Less than or equal to
 less_than_equal = (10 <= 5)  # Output: False 

Logical Operators:
Logical operators are used to combine conditional statements.

x = 10
y = 5  

# AND
 logical_and = (x > 0) and (y < 10)  # Output: True  

# OR
 logical_or = (x > 0) or (y > 10)  # Output: True  

# NOT
 logical_not = not (x > 0)  # Output: False 

Assignment Operators:
Assignment operators are used to assign values to variables.

# Assignment
 x = 10  

# Addition assignment
 x += 5  # Equivalent to x = x + 5  

# Subtraction
 assignment x -= 5  # Equivalent to x = x - 5  

# Multiplication assignment
 x *= 2  # Equivalent to x = x * 2  

# Division assignment
 x /= 2  # Equivalent to x = x / 2  

# Modulo assignment
 x %= 3  # Equivalent to x = x % 3  

# Floor Division assignment
 x //= 3  # Equivalent to x = x // 3  

# Exponentiation assignment
 x **= 2  # Equivalent to x = x ** 2 

Membership Operators:
Membership operators are used to test if a sequence is present in an object.

# In
 is_present = 'a' in ['a', 'b', 'c']  # Output: True  

# Not In
 is_not_present = 'd' not in ['a', 'b', 'c']  # Output: True 

Control Flow

Conditional Statements (if, elif, else)

  • Conditional statements are used to execute different blocks of code based on certain conditions.

if Statement:
The if statement checks a condition and executes a block of code if the condition is true.

x = 10

if x > 5:
     print("x is greater than 5") 

if-else Statement:
The if-else statement executes one block of code if the condition is true and another block if the condition is false.

x = 3

if x % 2 == 0:
     print("x is even")
else:
     print("x is odd") 

if-elif-else Statement:
The if-elif-else statement allows you to check multiple conditions and execute different blocks of code accordingly.

x = 0

if x > 0:
     print("x is positive")
elif x < 0:
     print("x is negative")
else:
     print("x is zero") 

Loops

  • Loops are used to iterate over a sequence of items or execute a block of code repeatedly.

for Loop:
The for loop iterates over a sequence (e.g., list, tuple, string) and executes a block of code for each item in the sequence.

numbers = [1, 2, 3, 4, 5]

for num in numbers:
     print(num) 

You can also use the range() function to generate a sequence of numbers for iteration:

for i in range(5):
     print(i) 

while Loop:
The while loop executes a block of code as long as a specified condition is true.

x = 1

while x <= 5:
     print(x)
     x += 1 

Loop Control Statements

  • Python provides loop control statements to modify the behavior of loops.

break:
Terminates the loop prematurely.

numbers = [1, 2, 3, 4, 5]
 for num in numbers:
     if num == 3:
         break
     print(num) 

continue:
Skips the current iteration of the loop and continues with the next iteration.

numbers = [1, 2, 3, 4, 5]

for num in numbers:
     if num == 3:
         continue
     print(num) 

else:
Executes a block of code when the loop completes normally (i.e., without encountering a break statement).

numbers = [1, 2, 3, 4, 5]

for num in numbers:
     print(num)
else:
     print("Loop completed without breaking") 

Functions

Functions in Python

  • Functions in Python are blocks of code that perform a specific task. They help in organizing code, making it more modular, reusable, and easier to understand.
  • Functions are a fundamental building block in Python programming, allowing you to encapsulate logic, promote code reuse, and enhance code readability.

Defining Functions:
You can define a function in Python using the def keyword followed by the function name and parentheses containing any parameters the function takes. The function body is indented and contains the code to be executed when the function is called.

def greet():
     print("Hello, world!") 

You can also define functions with parameters:

def greet(name):
     print("Hello, " + name + "!") 

Calling Python

  • To call a function, simply write its name followed by parentheses. If the function requires parameters, provide them within the parentheses.

greet()  # Output: Hello, world!

greet("John")  # Output: Hello, John! 

Returning Values

  • Functions can return values using the return keyword. You can then capture the returned value in a variable when calling the function.

def add(x, y):
     return x + y  

result = add(3, 5)
print(result)  # Output: 8 

Default Parameters

  • You can specify default values for parameters in a function. If a parameter is not provided when calling the function, it takes its default value.

def greet(name="world"):
     print("Hello, " + name + "!")  

greet()  # Output: Hello, world!
greet("John")  # Output: Hello, John! 

Keyword Arguments

  • You can also pass arguments to a function using keyword arguments, where each argument is explicitly assigned a value.

def greet(name, message):
     print(message + ", " + name + "!")  

greet(message="Good morning", name="John")  # Output: Good morning, John! 

Arbitrary Arguments

  • You can define functions that accept an arbitrary number of arguments using the *args syntax. These arguments are then treated as a tuple within the function.

def add(*args):
     total = 0
     for num in args:
         total += num
     return total  

print(add(1, 2, 3, 4, 5))  # Output: 15 

Docstrings

  • It's a good practice to include documentation for your functions using docstrings. Docstrings are triple-quoted strings placed immediately after the function definition, describing what the function does.

def greet(name):
     " " "
     Greets a person by name.

     Parameters:
         name (str): The name of the person to greet.
     " " "
     print("Hello, " + name + "!") 

Modules and Packages

Modules

  • A module is a file containing Python code. It can define functions, classes, and variables that can be used in other Python scripts. To create a module, save your Python code in a .py file with a meaningful name.

For example, let's create a module named math_operations.py:

# math_operations.py

def add(x, y):
     return x + y  

def subtract(x, y):
     return x - y 

You can then use this module in another Python script by importing it:

# main.py

import math_operations  

result = math_operations.add(5, 3)
print(result)  # Output: 8  

result = math_operations.subtract(5, 3)
print(result)  # Output: 2 

Packages

  • A package is a collection of modules organized in a directory structure. It can also contain sub-packages. To create a package, create a directory with an __init__.py file (which can be empty) and place your module files inside it.

For example, let's create a package named my_package:

my_package/
     __init__.py
     math_operations.py
     string_operations.py 

Inside math_operations.py, we have the same code as before. Now, let's create another module string_operations.py:

# string_operations.py

def concatenate_strings(str1, str2):
     return str1 + str2  

def capitalize_string(s):
     return s.capitalize() 

You can then use these modules from the package in your Python scripts:

# main.py

from my_package import math_operations, string_operations  

result = math_operations.add(5, 3)
print(result)  # Output: 8  

result = string_operations.concatenate_strings("Hello, ", "world!")
print(result)  # Output: Hello, world!  

result = string_operations.capitalize_string("hello")
print(result)  # Output: Hello 

Importing Specific Functions:
You can import specific functions from modules or packages using the from ... import ... syntax.

# main.py

from math_operations import add  

result = add(5, 3)
print(result)  # Output: 8 

Renaming Modules or Functions:
You can also rename modules or functions when importing them using the as keyword.

# main.py

from math_operations import add as addition  

result = addition(5, 3)
print(result)  # Output: 8 

File Handling

  • File handling in Python allows you to work with files on your computer's file system. You can read from files, write to files, and perform various other file-related operations. 
  • File handling in Python provides powerful tools for working with files efficiently and effectively, allowing you to read, write, and manipulate file contents as needed.

Opening a File

  • To open a file in Python, you use the built-in open() function. You specify the file path and the mode in which you want to open the file (read, write, append, etc.).

# Open a file for reading
file = open("example.txt", "r")  

# Open a file for writing (creates a new file if it doesn't exist)
file = open("example.txt", "w")  

# Open a file for appending (creates a new file if it doesn't exist)
file = open("example.txt", "a")  

# Open a file in binary mode
file = open("example.txt", "rb") 

Reading from a File

  • You can read the contents of a file using methods like read(), readline(), or readlines().

# Read the entire contents of the file
content = file.read()  

# Read a single line from the file
line = file.readline()  

# Read all lines into a list
lines = file.readlines() 

Writing to a File

  • You can write to a file using the write() method. If the file doesn't exist, it will be created.

# Write content to the file
file.write("Hello, world!n")  

# Write multiple lines to the file
file.writelines(["Line 1n", "Line 2n", "Line 3n"]) 

Closing a File

  • After you're done working with a file, it's essential to close it using the close() method. This ensures that any buffered data is written to the file and frees up system resources.

# Close the file
file.close() 

Using Context Managers (Recommended)

  • A better practice is to use Python's context managers (with statement) when working with files. Context managers automatically handle opening and closing files, ensuring that files are properly closed even if an error occurs.

with open("example.txt", "r") as file:
     content = file.read()  

# File is automatically closed when exiting the 'with' block 

Working with Binary Files

  • You can read and write binary files by specifying the binary mode ("rb" for reading, "wb" for writing, etc.).

with open("binary_file.bin", "rb") as file:
     data = file.read()

with open("new_binary_file.bin", "wb") as file:
     file.write(data) 

File Handling Exceptions

  • It's also essential to handle exceptions when working with files, especially when reading or writing files that may not exist or when encountering other errors.

try:
     with open("example.txt", "r") as file:
         content = file.read()
except FileNotFoundError:
     print("File not found")
except Exception as e:
     print("An error occurred:", e) 

Error Handling

  • Error handling in Python is essential for gracefully handling unexpected situations that may arise during program execution.
  •  It promotes robustness and reliability in your programs.

Basic Syntax

  • The basic syntax for a try-except block is as follows:

try:
     # Code that may raise an exception
     # ...
 except SomeException:
     # Code to handle the exception
     # ... 

Handling Specific Exceptions

  • You can specify the type of exception to catch using the except clause. You can catch specific exceptions or use a general except block to catch any exception.

try:
     # Code that may raise an exception
     x = 10 / 0  # This will raise a ZeroDivisionError
 except ZeroDivisionError:
     # Handling the specific exception
     print("Error: Division by zero occurred") 

Handling Multiple Exceptions

  • You can handle multiple exceptions using multiple except clauses or a single except clause with multiple exception types.

try:
     # Code that may raise an exception
     x = int("abc")  # This will raise a ValueError
except ValueError:
    # Handling a specific exception
     print("Error: Invalid value provided")
 except ZeroDivisionError:
     # Handling another specific exception
     print("Error: Division by zero occurred") 

Handling Any Exception

  • You can use a general except block to catch any exception that is not handled by previous except blocks.

try:
     # Code that may raise an exception
     x = int("abc")  # This will raise a ValueError
except ValueError:
     # Handling a specific exception
     print("Error: Invalid value provided")
except ZeroDivisionError:
     # Handling another specific exception
     print("Error: Division by zero occurred")
except:
     # Handling any other exception
     print("An error occurred") 

Using else and finally

  • ou can use the else block to execute code when no exceptions occur, and the finally block to execute cleanup code that should always run, regardless of whether an exception occurs or not.

try:
     # Code that may raise an exception
     x = 10 / 2
except ZeroDivisionError:
     # Handling the specific exception
     print("Error: Division by zero occurred")
else:
     # Code to execute if no exceptions occur
     print("No errors occurred, result:", x)
finally:
     # Cleanup code that always runs
     print("Cleanup code") 

Raising Exceptions

  • You can raise exceptions using the raise statement to indicate that an error has occurred in your code.

x = -1
if x < 0:
     raise ValueError("x cannot be negative") 

Object-Oriented Programming (OOP)

  • Object-Oriented Programming (OOP) is a programming paradigm that allows you to model real-world entities as objects, which have attributes (properties) and methods (functions). OOP provides concepts such as classes, objects, inheritance, polymorphism, and encapsulation.
  • Object-Oriented Programming in Python provides a powerful way to organize and structure code, promoting modularity, reusability, and maintainability. It allows you to model complex systems using intuitive and familiar real-world concepts.

Classes

  • Class: A class is a blueprint for creating objects. It defines the attributes and behaviors of objects. Classes are created using the class keyword.

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

      def greet(self):
         return f"Hello, my name is {self.name} and I am {self.age} years old." 

Object

  • Object: An object is an instance of a class. It represents a specific entity or instance of the class.

person1 = Person("Alice", 30)
person2 = Person("Bob", 25)  

print(person1.name)  # Output: Alice
print(person2.age)   # Output: 25  

print(person1.greet())  # Output: Hello, my name is Alice and I am 30 years old. 

Polymorphism

  • Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enables methods to behave differently based on the object they are called on.

class Animal:
     def make_sound(self):
         pass  

class Dog(Animal):
     def make_sound(self):
         return "Woof!"  

class Cat(Animal):
     def make_sound(self):
         return "Meow!"  

# Polymorphic behavior
def animal_sound(animal):
     return animal.make_sound()  

dog = Dog()
cat = Cat()  

print(animal_sound(dog))  # Output: Woof!
print(animal_sound(cat))  # Output: Meow! 

Encapsulation

  • Encapsulation refers to the bundling of data (attributes) and methods that operate on that data within a single unit (class). It hides the internal state of an object and restricts access to it from outside the class.

class BankAccount:
     def __init__(self, balance):
         self.__balance = balance

      def deposit(self, amount):
         self.__balance += amount

      def withdraw(self, amount):
         if self.__balance >= amount:
             self.__balance -= amount
             return amount
         else:
             return "Insufficient funds" 

Abstraction

  • Abstraction involves hiding complex implementation details and showing only the essential features of the object. It allows you to focus on what an object does rather than how it does it.

from abc import ABC, abstractmethod

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

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

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

Regular Expressions

  • Regular expressions (regex) are a powerful tool for pattern matching and text processing. Python provides the re module for working with regular expressions. Let's explore some common use cases.
  • Regular expressions are a versatile tool for handling complex text processing tasks. However, they can be challenging to master, so it's essential to practice and refer to documentation or tutorials as needed.

Basic Pattern Matching:

import re

pattern = r"hello"
text = "Hello, world! Hello, Python!"  

matches = re.findall(pattern, text)
print(matches)  # Output: ['hello', 'hello'] 

Metacharacters:

  • .: Matches any character except a newline. 
  • ^: Matches the start of the string. 
  • $: Matches the end of the string. 
  • *: Matches zero or more occurrences of the preceding character. 
  • +: Matches one or more occurrences of the preceding character. 
  • ?: Matches zero or one occurrence of the preceding character. 
  • : Escapes special characters.

Character Classes:

  • [abc]: Matches any single character from the set (a, b, or c). 
  • [^abc]: Matches any single character not in the set (a, b, or c). 
  • [a-z]: Matches any single lowercase letter. 
  • [A-Z]: Matches any single uppercase letter. 
  • [0-9]: Matches any single digit.

Quantifiers:

  • {n}: Matches exactly n occurrences of the preceding character. 
  • {n,}: Matches n or more occurrences of the preceding character. 
  • {n,m}: Matches between n and m occurrences of the preceding character.

Groups and Capturing:

pattern = r"(w+), (w+)"
text = "Doe, John"  

match = re.match(pattern, text)
if match:
     print(match.group(1))  # Output: Doe
     print(match.group(2))  # Output: John 

Search and Replace:

pattern = r"d+"
text = "Age: 30"  

new_text = re.sub(pattern, "25", text)
print(new_text)  # Output: Age: 25 

Flags:

pattern = r"hello"
text = "Hello, world!"  

match = re.search(pattern, text, re.IGNORECASE)
if match:
     print("Pattern found") 

Additional Functions:

  • re.match(): Matches a pattern only at the beginning of the string. 
  • re.search(): Searches for the first occurrence of a pattern in the string. 
  • re.findall(): Finds all occurrences of a pattern in the string. 
  • re.sub(): Replaces occurrences of a pattern in the string. 
  • re.split(): Splits the string based on a pattern.

Database Access

  • Interacting with databases is a crucial aspect of many applications. Python provides various libraries and frameworks for working with databases, including SQLite3 for lightweight local databases, MySQL and PostgreSQL for client-server databases, and ORMs like SQLAlchemy for object-relational mapping.
  • Using these tools, you can interact with databases in Python, whether it's for creating, reading, updating, or deleting data, making your applications more powerful and versatile. Choose the library or ORM that best fits your project requirements and preferences.

SQLite3

  • SQLite3 is a lightweight, serverless, self-contained SQL database engine that is often used for local development or small-scale applications.

Connecting to Database:

import sqlite3

# Connect to an SQLite database (creates a new database if it doesn't exist)
conn = sqlite3.connect('example.db')  

# Create a cursor object to execute SQL commands
cursor = conn.cursor() 

Executing SQL Commands:

# Execute SQL command to create a table
cursor.execute('''CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)''')  

# Execute SQL command to insert data into the table
cursor.execute('''INSERT INTO users (name, age) VALUES (?, ?)''', ('Alice', 30))  

# Commit the transaction
conn.commit() 

Querying Data:

# Execute SQL command to fetch data from the table
cursor.execute('''SELECT * FROM users''')  

# Fetch all rows
rows = cursor.fetchall()
for row in rows:
     print(row) 

Closing the Connection:

# Close the cursor and connection
cursor.close() conn.close() 

MySQL and PostgreSQL

  • For MySQL and PostgreSQL, you can use libraries like mysql-connector-python or psycopg2 respectively. The usage is similar to SQLite3, but you need to provide connection details like host, username, password, and database name.

SQLAlchemy

  • SQLAlchemy is a powerful ORM (Object-Relational Mapper) that provides a high-level interface for working with databases. It supports various database backends, including SQLite, MySQL, PostgreSQL, and others.

Connecting to Database:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker  

# Create an engine
engine = create_engine('sqlite:///example.db')  

# Create a sessionmaker
Session = sessionmaker(bind=engine)  

# Create a session
session = Session() 

Defining a Model:

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base  

Base = declarative_base()  

class User(Base):
     __tablename__ = 'users'

      id = Column(Integer, primary_key=True)
     name = Column(String)
     age = Column(Integer) 

Creating Tables:

# Create all defined tables
Base.metadata.create_all(engine) 

Inserting Data:

# Create a new user object
new_user = User(name='Bob', age=25)  

# Add the user to the session
session.add(new_user)  

# Commit the transaction
session.commit() 

Querying Data:

# Query all users
users = session.query(User).all()

for user in users:
     print(user.name, user.age) 

Closing the Session:

# Close the session
session.close() 

Web Scraping

  • Web scraping is the process of extracting data from websites. Python provides powerful libraries like BeautifulSoup and Scrapy for web scraping tasks.
  • Web scraping can be a powerful tool for gathering data, but it's essential to use it responsibly and ethically. Make sure to review the website's terms of service and use web scraping in compliance with legal and ethical guidelines.

BeautifulSoup

  • BeautifulSoup is a Python library for parsing HTML and XML documents. It makes it easy to extract data from web pages.

Installation:

pip install beautifulsoup4 

Basic Usage:

from bs4 import BeautifulSoup
import requests  

# Send an HTTP request to the URL
url = 'https://example.com'
response = requests.get(url)  

# Parse the HTML content
soup = BeautifulSoup(response.content, 'html.parser')  

# Find all < a > tags
links = soup.find_all('a')  

# Print the href attribute of each link
for link in links:
     print(link.get('href')) 

Scrapy

  • Scrapy is a powerful web crawling and web scraping framework for Python. It provides a more structured approach to web scraping and is suitable for more complex scraping tasks.

Installation:

pip install scrapy 

Creating a Scrapy Project:

scrapy startproject myproject
cd myproject 

Creating a Spider:

import scrapy

class MySpider(scrapy.Spider):
     name = 'myspider'
     start_urls = ['https://example.com']

     def parse(self, response):
         # Extract data using XPath or CSS selectors
         titles = response.css('h1::text').getall()

         # Yield the extracted data
         for title in titles:
             yield {'title': title} 

Running the Spider:

scrapy crawl myspider -o output.json 

Considerations

  • Respect Robots.txt: Always respect the website's robots.txt file and follow its guidelines to avoid legal issues. 
  • Use Delays: Add delays between requests to avoid overloading the server and getting banned. 
  • Use User-Agent: Set a custom user-agent to identify your scraper as a legitimate browser.