Table of Contents
Introduction to Python
What is Python?
Python is a versatile, high-level programming language designed for readability and simplicity. It employs indentation to define code blocks and supports procedural, object-oriented, and functional paradigms.
History of Python
Created by Guido van Rossum and released in 1991, Python has evolved through versions 2.x (legacy) to 3.x (current). Python 3.0 (2008) addressed design flaws and improved Unicode support.
Unique Features of Python
- Readable Syntax: Easy to learn and maintain.
- Rich Standard Library: Modules for file I/O, networking, threading.
- Cross-Platform: Windows, macOS, Linux support.
- Extensible: Integrate with C/C++ for performance-critical code.
- Community: Vast ecosystem of packages on PyPI.
Python 2 vs Python 3
- Print: Python 2 uses
print "Hello", Python 3 usesprint("Hello"). - Division:
5/2yields2in 2.x, but2.5in 3.x. - Unicode: Strings are Unicode by default in Python 3.
- Library Support: Most new libraries support Python 3 only.
Installing Python
Download the installer from python.org. On Windows, check "Add Python to PATH"; on macOS/Linux, use package managers like Homebrew or apt.
Setting Up Development Environment
Use an IDE or editor (e.g., VS Code, PyCharm). Configure linting (flake8) and formatting (black) extensions to enforce code style and catch errors early.
Important Programming Basics
Python Keywords and Indentation
Python reserves words like def, return, if, for. Indentation (standard 4 spaces) defines blocks instead of braces.
Comments
# Single-line comment
"""
Multi-line comment or docstring,
used to document modules and functions.
"""
Use # for inline comments. Triple-quoted strings at the top of modules, classes, or functions serve as documentation.
Basic Data Types
int: Integer numbers (e.g.,42).float: Decimal numbers (e.g.,3.14).str: Text strings (e.g.,"Hello").bool:TrueorFalse.None: Represents absence of value.
Variables
x = 10 # int assigned dynamically
name = "Umar" # string variable
is_active = True # boolean
Variables are created on assignment; types are inferred at runtime.
Operators
Arithmetic (+ - * / // % **), comparison (== != > < >= <=), logical (and, or, not), bitwise (& | ^ ~ << >>), membership (in, not in), identity (is, is not).
Strings
s = "Hello, Python!"
print(s[0], s[-1]) # H !
print(s.upper(), s.lower()) # HELLO, PYTHON! hello, python!
print(s.replace("Python", "World")) # Hello, World!
Strings are immutable. Use slicing (s[start:end]) and methods (.strip(), .split(), .find()) for manipulation.
Getting User Input
age = input("Enter your age: ")
age = int(age) # Convert to integer
print(f"You are {age} years old.")
input() returns a string. Convert types explicitly when needed.
First Program
print("Hello, World!")
Outputs text to the console using the print() function.
Loops & Control Statements
Control Structures
Simple if
x = 5
if x > 0:
print("Positive")
The if statement executes its block only if the condition is True.
if-else
if x % 2 == 0:
print("Even")
else:
print("Odd")
else runs when if is False.
Nested if
if x > 0:
if x < 10:
print("1-9")
else:
print(">=10")
You can nest if blocks for multiple checks.
if-elif-else
if x < 0:
print("Negative")
elif x == 0:
print("Zero")
else:
print("Positive")
elif allows chaining multiple conditions.
Loops
for loop
for i in range(5):
print(i)
range(n) yields 0 to n-1; for iterates over any sequence.
while loop
count = 0
while count < 5:
print(count)
count += 1
while repeats as long as its condition holds true. Ensure loop variables update to avoid infinite loops.
Break & Continue
for i in range(10):
if i == 5:
break # Exit loop completely
if i % 2 == 0:
continue # Skip to next iteration
print(i)
break stops the loop; continue skips the rest of the current iteration.
Functions, Modules & Packages
User-Defined Functions
def greet(name):
"""Return a greeting message for name."""
return f"Hello, {name}!"
message = greet("Umar")
print(message)
Functions encapsulate code for reuse. Docstrings describe their purpose.
Function Parameters & Scope
Parameters can be positional, keyword, default, or variable-length (*args, **kwargs). Variables inside are local; use global to modify globals.
Lambda Functions
add = lambda a, b: a + b
print(add(3, 4)) # Outputs 7
lambda creates small anonymous functions inline.
Modules & Namespaces
# my_module.py
PI = 3.14
def area(radius):
return PI * radius ** 2
# main.py
import my_module
print(my_module.area(5))
from my_module import area, PI
print(area(3))
Modules (.py files) group related code. Use import to access names. Namespaces prevent collisions.
The if __name__ == "__main__": block runs code only when the module is executed directly, not when imported.
Packages
Packages are directories with an __init__.py file, enabling hierarchical module organization. Install third-party packages via pip install package-name.
Data Structures in Python
Lists
fruits = ["apple", "banana", "cherry"]
fruits.append("date") # Add at end
print(fruits[1]) # Access by index
Lists are ordered, mutable collections. Support indexing, slicing, and methods like append(), remove(), sort().
Lists as Stacks
stack = []
stack.append(1)
stack.append(2)
print(stack.pop()) # LIFO behavior: outputs 2
Lists as Queues
from collections import deque
queue = deque()
queue.append(1)
queue.append(2)
print(queue.popleft()) # FIFO behavior: outputs 1
Tuples
point = (10, 20)
print(point[0]) # Immutable sequence
Tuples are ordered, immutable sequences used for fixed collections.
del Statement
del fruits[0] # Remove first item
del fruits # Delete entire list
del removes items or entire variables.
Iterators & Generators
# Iterator example
nums = [1, 2, 3]
it = iter(nums)
print(next(it)) # 1
# Generator example
def gen(n):
for i in range(n):
yield i
for val in gen(3):
print(val)
Iterators traverse collections. Generators create iterators with yield, saving memory.
Comprehensions
squares = [x**2 for x in range(5)]
evens = {x: x%2==0 for x in range(5)}
List, dict, and set comprehensions provide concise syntax for transforming iterables.
Ranges
for i in range(1, 10, 2):
print(i) # 1,3,5,7,9
range(start, stop, step) generates arithmetic sequences without storing them.
Dictionaries
student = {"name": "Bob", "age": 21}
print(student.get("name"))
student["grade"] = "A"
for key, val in student.items():
print(key, val)
Dictionaries map keys to values. Use dict.keys(), .values(), .items() to iterate.
Sets
s = {1, 2, 3}
s.add(4)
s.remove(2)
print(3 in s)
Sets store unique unordered elements. Support union (|), intersection (&), difference (-).
Exception Handling in Python
Raising Exceptions
raise ValueError("Invalid value")
raise manually triggers exceptions to signal errors.
Handling Exceptions
try:
x = int(input())
except ValueError:
print("Please enter an integer.")
else:
print("You entered", x)
finally:
print("Execution completed.")
try wraps code that may fail, except handles specific errors, else runs if no errors, finally always executes.
Custom Exceptions
class MyError(Exception):
"""Custom exception type."""
pass
raise MyError("Something went wrong")
Define custom exception classes by subclassing Exception.
Multithreading & Multiprocessing
Creating Threads
import threading
def worker(id):
print(f"Worker {id}")
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
Threads run concurrently within a process. Use threading.Thread to start them.
Thread Synchronization
lock = threading.Lock()
def safe_increment():
with lock:
# only one thread can execute this block at a time
global counter
counter += 1
Use locks to prevent race conditions when accessing shared resources.
Thread Pools
from concurrent.futures import ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=5) as executor:
executor.map(worker, range(5))
ThreadPoolExecutor manages a pool of worker threads.
Multiprocessing Module
from multiprocessing import Process
def task(id):
print(f"Process {id}")
if __name__ == "__main__":
processes = []
for i in range(3):
p = Process(target=task, args=(i,))
processes.append(p)
p.start()
multiprocessing runs separate processes, bypassing the GIL for CPU-bound tasks.
File Handling (I/O)
Reading Text Files
with open('data.txt', 'r') as f:
contents = f.read()
print(contents)
open() with 'r' mode reads entire file into memory.
Writing Text Files
with open('out.txt', 'w') as f:
f.write("Hello, file!\n")
'w' mode creates/truncates file. Use 'a' to append.
Binary Files & Pickle
import pickle
data = {'a': 1, 'b': 2}
with open('data.pkl', 'wb') as f:
pickle.dump(data, f)
with open('data.pkl', 'rb') as f:
loaded = pickle.load(f)
Pickle serializes objects to bytes; use 'wb' and 'rb' modes.
Collections in Python
The collections module provides specialized data types:
namedtuple: Tuple with named fields.deque: Double-ended queue for fast appends/pops.Counter: Counts hashable objects.OrderedDict: Dict that remembers insertion order.defaultdict: Dict with default value types.
Object Oriented Programming
Defining Classes
class Person:
"""A simple class representing a person."""
species = "Homo sapiens" # Class attribute
def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age
def greet(self):
print(f"Hello, I am {self.name}.")
__init__ initializes new objects. self refers to the instance.
Encapsulation
Use _protected and __private prefixes to signal intended privacy.
Inheritance
class Student(Person):
def __init__(self, name, age, student_id):
super().__init__(name, age)
self.student_id = student_id
super() calls base class methods.
Polymorphism
Different classes can implement the same method names, allowing interchangeable usage.
Built-In Class Attributes
__dict__: Attribute dictionary.__doc__: Docstring.__module__: Defining module.
Destroying Objects
del person_instance # Deletes reference to the object
Regular Expressions
What Are Regular Expressions?
Patterns to match character combinations in strings, handled by the re module.
match() vs search()
import re
m = re.match(r"\d+", "123abc") # Matches at start
s = re.search(r"abc", "123abc") # Searches anywhere
match() checks only at the beginning, search() scans the string.
Search and Replace
re.sub(r"\d+", "", "ID123") # Replaces digits with
sub() replaces all occurrences of the pattern.
Wildcards & Quantifiers
# . matches any char, * zero or more, + one or more
pattern = r"a.*?b" # Non-greedy match from a to b
re.findall(pattern, "aXb aYYb") # ['aXb', 'aYYb']
Quantifiers (?, *, +, {{m,n}}) control repetition. Use ? after to make them non-greedy.