Advance Python Program Unit IV
Advance Python Program Unit IV
Python, sockets are a way to enable communication between devices over a network. They
allow data to be sent and received between two endpoints, such as a client and a server.
Networking Protocols:
• UDP (User Datagram Protocol): Fast, connectionless protocol. Does not guarantee
delivery or order, making it ideal for real-time applications like gaming or streaming.
Python provides the socket module to work with networking protocols. Here's a simple
example:
TCP Server
import socket
print("Server is listening...")
print(f"Connected to {addr}")
print(f"Received: {data.decode()}")
server_socket.close()
TCP Client
import socket
print(f"Received: {response.decode()}")
client_socket.close()
UDP Server
import socket
while True:
UDP Client
import socket
print(f"Received: {response.decode()}")
client_socket.close()
With UDP, since there's no connection, the client can send data anytime, and the server
listens indefinitely.
class MyHandler(SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
httpd.serve_forever()
import requests
response = requests.get("http://127.0.0.1:8080")
This example sets up a simple HTTP server responding with an HTML message, and a client
fetching that page.
1. Multithreading in Python
import threading import time def print_numbers(): for i in range(5): print(f"Number {i}")
time.sleep(1) thread = threading.Thread(target=print_numb
import threading
import time
def print_numbers():
for i in range(5):
print(f"Number {i}")
time.sleep(1)
thread = threading.Thread(target=print_numbers)
thread.start()
2. Multiprocessing in Python
• Processes have separate memory spaces, avoiding global interpreter lock (GIL)
issues.
import multiprocessing
def worker_function(num):
print(f"Processing {num}")
def worker_function(num):
print(f"Processing {num}")
if __name__ == "__main__":
process.start()
process.start()
Thread synchronization
Python is crucial when multiple threads need to share resources safely. Since Python
threads run in the same memory space, improper synchronization can lead to race
conditions, where threads modify shared data unpredictably.
2. RLocks (threading.RLock) – Allows a thread to acquire the same lock multiple times.
5. Condition Variables (threading.Condition) – Used when one thread waits for another
to meet certain conditions.
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(1000000):
with lock: # Lock ensures only one thread modifies 'counter' at a time
counter += 1
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
Without the lock, counter may not reach the expected value due to race conditions.
Shared memory
Python allows multiple processes to access and modify the same data without needing
expensive inter-process communication (IPC). The multiprocessing module provides tools to
facilitate shared memory.
The Value and Array classes create shared variables that multiple processes can modify.
import multiprocessing
def increment(shared_counter):
for _ in range(1000000):
shared_counter.value += 1
process1.start()
process2.start()
process1.join()
process2.join()
import multiprocessing.shared_memory
import numpy as np
array[:] = np.arange(256)
shm.close()
Priority queues
Python allow elements to be processed based on priority rather than order of arrival. They
are especially useful in tasks like scheduling, pathfinding algorithms (like Dijkstra’s), and task
management.
Python’s heapq module provides an efficient way to implement a priority queue using a
min-heap.
import heapq
queue = []
while queue:
priority, task = heapq.heappop(queue)
Output:
2. Using queue.PriorityQueue
import queue
pq = queue.PriorityQueue()
This behaves similarly to heapq, but with built-in synchronization for multithreading.