0% found this document useful (0 votes)
1 views

python unit 3 module 1 2

Uploaded by

Omkar Gawali
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1 views

python unit 3 module 1 2

Uploaded by

Omkar Gawali
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 37

Python Programming

UNIT 3
Module 1
# Multithreading: Types of Multi-Threading in
python , process base tasking , Thread base tasing ,
creating thread by using extending thread class.
In Python, multithreading allows multiple threads (smaller
units of a process) to run concurrently, enabling efficient CPU
utilization and faster execution for certain tasks.
1. Process-based Tasking
 Definition: Process-based tasking (also known as
multiprocessing) involves running separate processes in
parallel. Each process has its own memory space and
runs independently of others. In Python, the
multiprocessing module is used for process-based
parallelism.
 Best Use Case: Useful when the tasks are CPU-bound
(i.e., they require a lot of computation). Since each
process runs in its own memory space, Python’s Global
Interpreter Lock (GIL) doesn’t affect them, making them
efficient for CPU-intensive tasks.
 Example:
Python code
from multiprocessing import Process

def task():
print("Process running")

if __name__ == "__main__":
process = Process(target=task)
process.start()
process.join()
2. Thread-based Tasking
 Definition: Thread-based tasking (multithreading)
involves multiple threads within the same process.
Threads share the same memory space and resources, so
they can communicate more easily but also require
careful handling to avoid issues like data races.
 Best Use Case: Useful for I/O-bound tasks, such as
network requests or file I/O, where threads can be
suspended while waiting for external resources. The GIL
limits true parallelism for CPU-bound tasks in Python, so
multithreading is generally less effective for such tasks.
 Example:
Python code
from threading import Thread

def task():
print("Thread running")

thread = Thread(target=task)
thread.start()
thread.join()
3. Creating Threads by Extending the Thread Class
 Definition: In this approach, you define a new class that
inherits from the Thread class, overriding its run()
method to define the thread's behavior.
 Advantages: This method is useful if you want to create
specialized threads with additional attributes and
methods beyond a basic function.
 Example:
Python code
from threading import Thread

class MyThread(Thread):
def run(self):
print("Thread running by extending Thread class")

my_thread = MyThread()
my_thread.start()
my_thread.join()

# Thread Identification Number .


In Python, each thread has a unique identification number
(ID) which can be retrieved to distinguish between different
threads running within the same process. This thread ID can
be useful for debugging, logging, or managing threads
individually.
Getting Thread Identification Number
Python provides the ident attribute in the Thread class to get
the thread's unique identifier. You can use this attribute after
the thread has started.
Here's an example of how to retrieve a thread's ID:
Python code
import threading

def task():
print(f"Thread ID: {threading.get_ident()}")

# Creating and starting the thread


thread = threading.Thread(target=task)
thread.start()
thread.join()
Explanation of the Key Functions:
 threading.get_ident(): Returns the ID of the current
thread. If called within a thread, it will return that
thread's unique ID.
 thread.ident: This attribute is available on a Thread
object and represents the thread’s unique identifier. It
will be None if the thread has not yet been started.
Example with Multiple Threads:
If you have multiple threads and want to print each thread’s
ID:
Python code
import threading

def task():
print(f"Running in Thread ID: {threading.get_ident()}")

# Creating multiple threads


threads = [threading.Thread(target=task) for _ in range(3)]

for thread in threads:


thread.start()

for thread in threads:


thread.join()
Important Notes
 The thread ID is assigned by the operating system and is
unique for each thread within a single process.
 Thread IDs are reused by the OS once threads have
terminated, so they should not be stored or relied on after
the thread ends.

# Daemon Threads .
In Python, a daemon thread is a thread that runs in the
background and is designed to terminate automatically when
all non-daemon threads (main threads or foreground threads)
have completed their work. Daemon threads are typically used
for background tasks that should not prevent a program from
exiting, such as monitoring services or periodic maintenance
tasks.
Characteristics of Daemon Threads
1. Background Execution: Daemon threads run in the
background, and Python does not wait for them to
complete before exiting the program.
2. Automatic Termination: When the main program exits,
any daemon threads are also terminated, regardless of
whether they have finished their tasks.
3. Use Case: Ideal for tasks like garbage collection,
logging, monitoring, and housekeeping that should not
keep the program alive.
Creating Daemon Threads
To make a thread a daemon, set its daemon attribute to True
before starting it. By default, threads are non-daemon (i.e.,
daemon=False).
Here’s how you can create a daemon thread:
Python code
import threading
import time

def background_task():
while True:
print("Background task running")
time.sleep(1)

# Creating a daemon thread


daemon_thread = threading.Thread(target=background_task)
daemon_thread.daemon = True # Set as daemon
daemon_thread.start()

# Main program continues to run


print("Main program running")
time.sleep(3)
print("Main program ending")
In this example, the daemon thread will be terminated when
the main program exits, even if it hasn’t completed its while
loop.
Checking If a Thread Is a Daemon
You can check if a thread is a daemon using the isDaemon()
method:
Python code
print(daemon_thread.isDaemon()) # Output: True
Important Points to Remember
1. Set Before Start: The daemon attribute must be set
before calling start(). Attempting to change it after
starting the thread will raise a RuntimeError.
2. No Guarantees on Cleanup: Since daemon threads are
terminated abruptly when the main program exits, they
may not have a chance to perform any cleanup tasks.
3. Avoid for Critical Tasks: Avoid using daemon threads
for tasks that require guaranteed completion, such as
saving data to a file or closing resources.
Example with Multiple Threads
Here’s an example that illustrates the behavior of both
daemon and non-daemon threads:
Python code
import threading
import time

def non_daemon_task():
print("Non-daemon task starting")
time.sleep(5)
print("Non-daemon task completed")

def daemon_task():
while True:
print("Daemon task running")
time.sleep(1)

# Create and start a non-daemon thread


non_daemon_thread =
threading.Thread(target=non_daemon_task)
non_daemon_thread.start()

# Create and start a daemon thread


daemon_thread = threading.Thread(target=daemon_task)
daemon_thread.daemon = True
daemon_thread.start()

print("Main program running")


time.sleep(2)
print("Main program ending")

# Synchronization of Threading .
Thread synchronization in Python is crucial for coordinating
access to shared resources among multiple threads, ensuring
that they don't interfere with each other and lead to
inconsistent or unexpected results. Python provides several
synchronization mechanisms, such as Locks, RLocks,
Semaphores, and Events.
1. Lock (Mutex)
A Lock (also known as a mutex) is the most common
synchronization primitive in Python's threading module. It
allows only one thread to access a critical section at a time,
preventing race conditions when multiple threads try to
modify shared data.
 Usage: Use a Lock to wrap sections of code that should
only be executed by one thread at a time.
python code
import threading
# Shared resource
counter = 0
lock = threading.Lock()

def increment():
global counter
for _ in range(1000):
lock.acquire() # Acquire the lock
counter += 1 # Critical section
lock.release() # Release the lock

# Create threads
threads = [threading.Thread(target=increment) for _ in
range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()

print("Counter:", counter) # Expected output: 10000


In this example, each thread acquires the lock before
modifying counter and releases it afterward, ensuring only
one thread changes counter at a time.
2. RLock (Reentrant Lock)
An RLock (Reentrant Lock) is similar to a regular Lock, but
it can be acquired multiple times by the same thread without
causing a deadlock. This is helpful in situations where a
thread needs to call a function that also acquires the same
lock.
 Usage: Use RLock when you need reentrant locking
within the same thread.
python code
import threading

lock = threading.RLock()

def task():
with lock:
print("First level lock acquired")
with lock:
print("Second level lock acquired")

thread = threading.Thread(target=task)
thread.start()
thread.join()
In this example, the same thread acquires lock twice without
issue, as RLock allows recursive acquisition within the same
thread.
3. Semaphore
A Semaphore allows controlling access to a resource by a
fixed number of threads. You initialize it with a count, and
each acquire() call decreases the count. When the count
reaches zero, other threads trying to acquire the semaphore
will block until the count is positive again.
 Usage: Use a Semaphore when you need to limit access
to a shared resource by a certain number of threads.
Python code
import threading
import time

semaphore = threading.Semaphore(3) # Allows 3 threads at a


time

def task():
with semaphore:
print(f"{threading.current_thread().name} acquired the
semaphore")
time.sleep(2)
print(f"{threading.current_thread().name} released the
semaphore")

# Creating multiple threads


threads = [threading.Thread(target=task) for _ in range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
Here, only three threads can acquire the semaphore and run
the task function simultaneously. The others wait until a
thread releases the semaphore.
4. Event
An Event is a simple way to have one or more threads wait
until they receive a signal to continue. Unlike Lock or
Semaphore, an Event does not control access to a resource but
instead manages a flag that threads can check.
 Usage: Use an Event when you need to have threads wait
for some condition to become true.
Python code
import threading
import time

event = threading.Event()

def waiter():
print("Waiting for event to be set")
event.wait() # Block until the event is set
print("Event is set, continuing")

def setter():
time.sleep(2)
event.set() # Set the event after 2 seconds
print("Event has been set")

# Creating threads
thread1 = threading.Thread(target=waiter)
thread2 = threading.Thread(target=setter)

thread1.start()
thread2.start()
thread1.join()
thread2.join()
In this example, waiter() blocks at event.wait() until setter()
sets the event. This approach is useful when you want to delay
thread execution until a specific condition occurs.
5. Condition
A Condition variable allows threads to wait for certain
conditions to be met before they proceed. Conditions are often
used with Locks to allow more fine-grained control over when
threads are allowed to proceed.
 Usage: Use a Condition when a thread needs to wait for
a specific condition to be met.
Python code
import threading

condition = threading.Condition()
shared_data = []

def consumer():
with condition:
print("Consumer waiting for data")
condition.wait() # Wait for data to be available
print("Consumer received data:", shared_data.pop())

def producer():
with condition:
shared_data.append("Data")
print("Producer added data")
condition.notify() # Notify waiting threads that data is
available

# Creating threads
consumer_thread = threading.Thread(target=consumer)
producer_thread = threading.Thread(target=producer)

consumer_thread.start()
producer_thread.start()
consumer_thread.join()
producer_thread.join()
Here, the consumer waits until the producer adds data to
shared_data and calls notify() to unblock it.
# Synchronization by using the lock concept .
Using the Lock mechanism in Python is one of the simplest
ways to achieve thread synchronization. A Lock ensures that
only one thread can access a shared resource or critical section
of code at any given time, preventing race conditions.
How Lock Works
1. Acquire: Before accessing a shared resource, a thread
calls lock.acquire() to obtain exclusive access. If another
thread has already acquired the lock, the requesting
thread will wait (or block) until the lock becomes
available.
2. Release: After finishing with the shared resource, the
thread calls lock.release() to release the lock, allowing
other waiting threads to proceed.
Example of Synchronization Using Lock
Suppose we have a shared resource, such as a counter, that
multiple threads want to increment. Without proper
synchronization, the result could be unpredictable, as threads
may read and modify the counter simultaneously. Using a
lock, we can ensure that only one thread increments the
counter at a time.
Python code
import threading

# Shared resource
counter = 0

# Lock to synchronize access to counter


lock = threading.Lock()
def increment():
global counter
for _ in range(1000):
lock.acquire() # Acquire the lock before accessing the
shared resource
try:
counter += 1 # Critical section
finally:
lock.release() # Release the lock after the critical
section

# Creating multiple threads


threads = [threading.Thread(target=increment) for _ in
range(10)]

# Starting threads
for thread in threads:
thread.start()

# Waiting for all threads to complete


for thread in threads:
thread.join()

print("Final Counter Value:", counter) # Expected output:


10000
Explanation of the Code
1. Define a Lock: lock = threading.Lock() creates a lock
object.
2. Acquire the Lock: Each thread acquires the lock before
entering the critical section (counter += 1). This ensures
that only one thread can modify counter at a time.
3. Release the Lock: The lock.release() call releases the
lock after updating the counter, allowing another thread
to acquire it.
4. Using try-finally: Wrapping the critical section in a try-
finally block ensures that the lock is released even if an
error occurs, preventing a deadlock.
Benefits of Using Locks
 Prevent Race Conditions: Locks prevent multiple
threads from modifying shared resources simultaneously.
 Ensure Data Integrity: By controlling access to critical
sections, locks help ensure that the program behaves as
expected.
Drawbacks of Using Locks
 Blocking: Threads that attempt to acquire a locked
resource must wait, which can reduce parallelism.
 Deadlock Risk: If locks are not managed carefully, a
program could enter a deadlock state where two or more
threads are waiting indefinitely for each other’s
resources.
Alternative Lock Syntax: Using with
You can also use with lock syntax to handle locking and
releasing automatically, which is simpler and more readable.
This approach eliminates the need for explicit calls to
acquire() and release().
Python code
def increment():
global counter
for _ in range(1000):
with lock: # Automatically acquires and releases the
lock
counter += 1
This with syntax automatically acquires the lock at the start of
the block and releases it when exiting the block, even if an
error occurs.
Example with Multiple Shared Resources
If you need to synchronize multiple shared resources, use
separate locks for each resource to avoid deadlocks and
reduce contention.
Python code
counter1 = 0
counter2 = 0
lock1 = threading.Lock()
lock2 = threading.Lock()

def increment_counters():
global counter1, counter2
for _ in range(1000):
with lock1:
counter1 += 1
with lock2:
counter2 += 1
Using lock1 and lock2 separately for counter1 and counter2
ensures independent access to each variable, enhancing
parallelism.

# Lock and Rlock.


In Python, Locks and RLocks (Reentrant Locks) are
synchronization primitives provided by the threading module
that help manage access to shared resources in multi-threaded
applications. While both serve similar purposes, they have
distinct characteristics and use cases.
1. Lock
A Lock is a basic synchronization primitive that can be in
either a locked or unlocked state. When a thread acquires a
lock, no other thread can acquire that lock until it is released.
Key Characteristics:

 Exclusive Access: Only one thread can hold the lock at a


time. Other threads that attempt to acquire the lock will
block until it is released.
 Non-Reentrant: If a thread that already holds a lock
tries to acquire it again, it will lead to a deadlock since
the lock does not allow recursive acquisition.
Example of Using Lock
python code
import threading

# Shared resource
counter = 0
lock = threading.Lock()

def increment():
global counter
for _ in range(1000):
lock.acquire() # Acquire the lock
try:
counter += 1 # Critical section
finally:
lock.release() # Release the lock

# Creating and starting multiple threads


threads = [threading.Thread(target=increment) for _ in
range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()

print("Final Counter Value:", counter) # Expected output:


10000
2. RLock (Reentrant Lock)
An RLock is a specialized type of lock that allows a thread to
acquire it multiple times without causing a deadlock. It keeps
track of the number of times it has been acquired and requires
an equal number of releases before it can be considered free.
Key Characteristics:

 Reentrant: A thread can acquire the lock multiple times


without blocking itself. It must call release() the same
number of times to fully release the lock.
 Use Cases: Useful in scenarios where a thread may need
to call a function that requires the same lock, such as
when dealing with recursive function calls.
Example of Using RLock
python code
import threading

# Shared resource
counter = 0
rlock = threading.RLock()

def increment():
global counter
for _ in range(1000):
rlock.acquire() # Acquire the RLock
try:
counter += 1 # Critical section
finally:
rlock.release() # Release the RLock

def nested_increment():
rlock.acquire() # Acquire the RLock for the outer function
try:
increment() # Call a function that also acquires the same
RLock
finally:
rlock.release() # Release the RLock

# Creating and starting multiple threads


threads = [threading.Thread(target=nested_increment) for _ in
range(5)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()

print("Final Counter Value:", counter) # Expected output:


5000
Differences Between Lock and RLock
Feature Lock RLock
Reentrancy Non-reentrant Reentrant
One thread at a Can be acquired multiple
Acquisition
time times by the same thread
Deadlock Higher risk for Lower risk for recursive
Risk recursive calls calls
Simple mutual Recursive functions or
Use Case
exclusion complex scenarios

# Bounded Semaphore , Inter Thread


Communication .
Bounded Semaphore in Python
A bounded semaphore is a synchronization primitive that
restricts the number of concurrent threads accessing a
particular resource. Unlike a regular semaphore, which can be
incremented indefinitely, a bounded semaphore has a fixed
upper limit set at its initialization. This can help prevent
resource overcommitment by ensuring that the number of
concurrent accesses does not exceed a specified count.
Key Characteristics of Bounded Semaphore

 Fixed Limit: You set the maximum number of threads


that can acquire the semaphore simultaneously when you
create it.
 Blocking Behavior: If the limit is reached, any
additional threads trying to acquire the semaphore will
block until another thread releases it.
 Useful for Resource Management: Bounded
semaphores are particularly useful when dealing with
limited resources like database connections, file handles,
etc.
Example of Using Bounded Semaphore
Python code
import threading
import time

# Create a bounded semaphore with a limit of 3


bounded_semaphore = threading.BoundedSemaphore(3)

def worker(thread_id):
print(f"Thread {thread_id} is waiting to acquire the
semaphore.")
with bounded_semaphore: # Acquire the semaphore
print(f"Thread {thread_id} has acquired the
semaphore.")
time.sleep(2) # Simulate some work
print(f"Thread {thread_id} has released the semaphore.")

# Create and start multiple threads


threads = [threading.Thread(target=worker, args=(i,)) for i in
range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()

print("All threads have completed.")


Inter-Thread Communication in Python
Inter-thread communication allows threads to communicate
with each other, share data, and coordinate actions. Python
provides several mechanisms for this purpose, including:
1. Condition Variables: Allow threads to wait for certain
conditions to be met before proceeding.
2. Events: Used to signal between threads when a condition
has occurred.
3. Queues: Provide a thread-safe way to exchange
information between threads.
1. Using Condition Variables

Condition variables are used to make one or more threads wait


until a particular condition occurs. They are often used in
conjunction with a Lock to protect shared state.
Example of Condition Variables:
Python code
import threading
import time

# Shared resource
queue = []
condition = threading.Condition()

def producer():
global queue
for i in range(5):
time.sleep(1) # Simulate work
with condition:
queue.append(i)
print(f"Produced {i}")
condition.notify() # Notify one waiting thread
def consumer():
global queue
for _ in range(5):
with condition:
while not queue:
condition.wait() # Wait until an item is produced
item = queue.pop(0)
print(f"Consumed {item}")

# Create and start threads


producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

producer_thread.join()
consumer_thread.join()

print("Finished producing and consuming.")


2. Using Events

Events are simpler than condition variables. An event can be


set to true or false, allowing threads to wait for it to be set.
Example of Events:
Python code
import threading
import time

event = threading.Event()
def worker():
print("Worker thread waiting for event...")
event.wait() # Block until the event is set
print("Worker thread proceeding after event is set.")

def main():
thread = threading.Thread(target=worker)
thread.start()
time.sleep(2)
print("Main thread setting event.")
event.set() # Set the event to let the worker thread proceed

main()
3. Using Queues

The queue.Queue class provides a thread-safe FIFO queue


that can be used for inter-thread communication.
Example of Using Queues:
Python code
import threading
import queue
import time

def producer(q):
for i in range(5):
time.sleep(1)
q.put(i) # Put an item in the queue
print(f"Produced {i}")

def consumer(q):
while True:
item = q.get() # Get an item from the queue
if item is None:
break # Exit if None is received
print(f"Consumed {item}")
q.task_done() # Signal that the item has been processed

# Create a Queue
q = queue.Queue()

# Create and start threads


producer_thread = threading.Thread(target=producer,
args=(q,))
consumer_thread = threading.Thread(target=consumer,
args=(q,))

producer_thread.start()
consumer_thread.start()

producer_thread.join()
q.put(None) # Send a signal to the consumer to exit
consumer_thread.join()

print("Finished producing and consuming.")

# Types Of Queues.
In Python, the queue module provides several types of queue
classes that are thread-safe and can be used for inter-thread
communication.
1. Queue (FIFO Queue)
The Queue class implements a first-in, first-out (FIFO) queue.
It is suitable for scenarios where the order of processing is
important. The first element added to the queue will be the
first one to be removed.
Key Methods:

 put(item): Adds an item to the queue.


 get(): Removes and returns an item from the queue. This
operation blocks if the queue is empty.
 qsize(): Returns the approximate size of the queue.
 empty(): Returns True if the queue is empty, otherwise
False.
 full(): Returns True if the queue is full, otherwise False.
Example:
python code
import queue
import threading
import time

def producer(q):
for i in range(5):
time.sleep(1)
q.put(i)
print(f"Produced {i}")

def consumer(q):
while True:
item = q.get()
if item is None:
break
print(f"Consumed {item}")
q.task_done()
# Create a FIFO queue
q = queue.Queue()

# Create and start threads


producer_thread = threading.Thread(target=producer,
args=(q,))
consumer_thread = threading.Thread(target=consumer,
args=(q,))

producer_thread.start()
consumer_thread.start()

producer_thread.join()
q.put(None) # Send signal to consumer to exit
consumer_thread.join()

print("Finished producing and consuming.")


2. LifoQueue (Last-In, First-Out Queue)
The LifoQueue class implements a last-in, first-out (LIFO)
queue, also known as a stack. The last element added to the
queue will be the first one to be removed.
Key Methods:

Similar to Queue, including:


 put(item): Adds an item to the queue.
 get(): Removes and returns the most recently added item
from the queue.
Example:
python code
import queue

# Create a LIFO queue


lifo_queue = queue.LifoQueue()

# Adding items to the LIFO queue


lifo_queue.put(1)
lifo_queue.put(2)
lifo_queue.put(3)

# Retrieving items from the LIFO queue


print(lifo_queue.get()) # Outputs: 3
print(lifo_queue.get()) # Outputs: 2
print(lifo_queue.get()) # Outputs: 1
3. PriorityQueue
The PriorityQueue class implements a priority queue, which
allows items to be retrieved in priority order (the smallest item
is retrieved first). Items must be tuples of the form (priority,
item), where lower numbers indicate higher priority.
Key Methods:

Similar to Queue, including:


 put(item): Adds an item to the queue with priority.
 get(): Removes and returns the highest priority item.
Example:
python code
import queue

# Create a priority queue


priority_queue = queue.PriorityQueue()

# Adding items with priorities


priority_queue.put((2, "Task 2"))
priority_queue.put((1, "Task 1"))
priority_queue.put((3, "Task 3"))

# Retrieving items in priority order


while not priority_queue.empty():
print(priority_queue.get()[1]) # Outputs: Task 1, Task 2,
Task 3
4. SimpleQueue
The SimpleQueue class is a simpler version of the FIFO
queue without any maximum size limitation. It is meant for
simpler use cases where you need a basic queue without the
overhead of maintaining other features.
Key Methods:

Similar to Queue, including:


 put(item): Adds an item to the queue.
 get(): Removes and returns an item from the queue.
Example:
python code
import queue

# Create a SimpleQueue
simple_queue = queue.SimpleQueue()

# Adding items to the SimpleQueue


simple_queue.put(1)
simple_queue.put(2)
simple_queue.put(3)

# Retrieving items from the SimpleQueue


print(simple_queue.get()) # Outputs: 1
print(simple_queue.get()) # Outputs: 2
print(simple_queue.get()) # Outputs: 3
UNIT 3
Module 2
# Python Database Programming: Standard Steps
for Python DB Programming.
Python database programming typically involves several
standard steps that guide you through connecting to a
database, executing SQL queries, and handling results.
1. Choose a Database and Install Required Libraries
Before you begin, choose the database management system
(DBMS) you want to work with (e.g., SQLite, MySQL,
PostgreSQL). Install the appropriate Python library for your
database:
 SQLite: Built-in support with the sqlite3 module (no
installation needed).
 MySQL: Use mysql-connector-python or PyMySQL.
 PostgreSQL: Use psycopg2 or SQLAlchemy.
bash code
# Example: Install MySQL connector
pip install mysql-connector-python

# Example: Install PostgreSQL connector


pip install psycopg2
2. Import the Required Modules
Import the necessary modules at the beginning of your Python
script.
python code
import sqlite3 # For SQLite
import mysql.connector # For MySQL
import psycopg2 # For PostgreSQL
3. Establish a Database Connection
Create a connection to the database using the appropriate
connection method provided by the library.
python code
# SQLite
conn = sqlite3.connect('example.db')

# MySQL
conn = mysql.connector.connect(
host='localhost',
user='your_username',
password='your_password',
database='your_database'
)

# PostgreSQL
conn = psycopg2.connect(
dbname='your_database',
user='your_username',
password='your_password',
host='localhost'
)
4. Create a Cursor Object
A cursor object allows you to execute SQL queries and fetch
results.
python code
cursor = conn.cursor()
5. Execute SQL Queries
You can execute various SQL commands such as CREATE,
INSERT, SELECT, UPDATE, and DELETE.
python code
# Create a table
cursor.execute('''CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER)''')

# Insert data
cursor.execute("INSERT INTO users (name, age) VALUES
(%s, %s)", ('Alice', 30))

# Select data
cursor.execute("SELECT * FROM users")
6. Fetch Results
Depending on the type of query, you can fetch results using
methods like fetchone(), fetchall(), or fetchmany(size).
python code
# Fetch all results
rows = cursor.fetchall()
for row in rows:
print(row)
7. Commit Changes (for Write Operations)
If you have executed INSERT, UPDATE, or DELETE
statements, make sure to commit the changes to the database.
python code
conn.commit() # Save changes
8. Handle Exceptions
Use try-except blocks to handle any potential errors that may
occur during database operations.
python code
try:
# Your database operations here
except Exception as e:
print(f"An error occurred: {e}")
finally:
# Clean up
cursor.close()
conn.close()
9. Close the Connection
Always ensure that you close the cursor and connection when
you are done to free up resources.
python code
cursor.close()
conn.close()
Full Example
Here’s a complete example that demonstrates the standard
steps in Python database programming using SQLite:
python code
import sqlite3

# Step 3: Establish a connection


conn = sqlite3.connect('example.db')

try:
# Step 4: Create a cursor object
cursor = conn.cursor()

# Step 5: Create a table


cursor.execute('''CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER)''')

# Step 6: Insert data


cursor.execute("INSERT INTO users (name, age)
VALUES (?, ?)", ('Alice', 30))
cursor.execute("INSERT INTO users (name, age)
VALUES (?, ?)", ('Bob', 25))

# Step 7: Commit changes


conn.commit()

# Step 8: Select data


cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
print(row)

except Exception as e:
print(f"An error occurred: {e}")
finally:
# Step 9: Clean up
cursor.close()
conn.close()

You might also like