Python is one of the most popular programming languages in
the world. It is known for its simplicity, readability, and versatility,
making it a great choice for beginners as well as professionals.
- Easy
to Learn: Python uses a clean and simple syntax that looks very close
to English.
- General
Purpose: It can be used for web development, data analysis, artificial
intelligence, machine learning, automation, game development, and more.
- Cross-Platform:
Python works on Windows, macOS, Linux, and even mobile devices.
- Large
Community: Millions of developers contribute to Python, creating
thousands of libraries and frameworks.
1. Introduction to Python
Python is one of the most popular programming languages in the world. It is easy to learn, simple to use, and extremely powerful. Python is used in web development, artificial intelligence, data science, machine learning, automation, and more.
1.1 Why Python?
- Beginner-friendly syntax (almost like English).
- Supports multiple programming paradigms (object-oriented, procedural, functional).
- Has a huge standard library and third-party modules.
- Cross-platform: works on Windows, Linux, macOS.
1.2 Example: First Python Command
You can try your first Python program using the print() function:
print("Hello, World!")
When you run this, Python will display:
Hello, World!
1.3 Python as a Calculator
>>> 10 + 5
15
>>> 8 * 3
24
>>> 20 / 4
5.0
2. Installation & Setup
Before using Python commands, you need to install Python on your system.
2.1 Downloading Python
Go to the official website: python.org and download the latest version suitable for your operating system.
2.2 Installation on Windows
- Run the downloaded installer.
- Check the box "Add Python to PATH".
- Click Install Now.
2.3 Installation on macOS
Mac usually comes with Python pre-installed. You can check the version by running:
python3 --version
2.4 Installation on Linux
sudo apt update
sudo apt install python3
2.5 Verifying Installation
Once installed, check if Python is working:
python --version
# or
python3 --version
2.6 Running Python
You can run Python in two main ways:
- Interactive mode: Type
pythonorpython3in terminal/command prompt. - Script mode: Write your code in a file (e.g.,
hello.py) and run it with:
python hello.py
3. Basic Syntax
Before writing complex programs, you should understand the basic syntax rules of Python. These rules define how Python code must be written and interpreted.
3.1 Case Sensitivity
Python is case-sensitive. That means Print and print are considered different.
print("hello") # works
Print("hello") # error
3.2 Indentation
Unlike other programming languages that use braces { }, Python uses indentation (spaces or tabs) to define code blocks.
if 5 > 2:
print("Five is greater than two")
3.3 Comments
Comments are ignored by Python but help humans understand the code.
- Single-line comment: Starts with
# - Multi-line comment: Written using triple quotes
''' '''or""" """
# This is a single-line comment
"""
This is a
multi-line comment
in Python
"""
3.4 Print Statements
The print() function is used to display output.
print("Hello, Python!")
print(10 + 20)
3.5 Python Identifiers
Identifiers are names given to variables, functions, classes, etc. Rules:
- Must start with a letter or underscore.
- Cannot start with a digit.
- Case-sensitive (
ageandAgeare different). - Cannot use reserved keywords (like
if,while, etc.).
3.6 Example Program
Here’s a simple Python program that uses comments, variables, and print statements:
# Example program
name = "Arjun"
age = 25
print("My name is", name)
print("I am", age, "years old")
4. Variables in Python
Variables are containers that store data values. In Python, you don’t need to declare the type of variable explicitly — Python automatically assigns the type based on the value.
4.1 Creating Variables
Assign a value using the = operator.
x = 10
y = "Hello"
z = 3.14
print(x)
print(y)
print(z)
4.2 Variable Naming Rules
- Names must begin with a letter (a–z, A–Z) or an underscore (_).
- Names cannot start with a digit.
- Names are case-sensitive (
name,Name, andNAMEare different). - Cannot use Python keywords (e.g.,
if,for,class).
myvar = "John"
my_var = "Doe"
_myVar = "Python"
MyVar = 25
4.3 Multiple Assignments
You can assign values to multiple variables in one line.
a, b, c = 5, 10, 15
print(a, b, c)
You can also assign the same value to multiple variables:
x = y = z = "Python"
print(x, y, z)
4.4 Variable Types
Python supports different types of variables:
- Numbers: integers, floats, complex
- Strings: text inside quotes
- Boolean: True or False
- Collections: list, tuple, dictionary, set
x = 100 # integer
y = 25.75 # float
z = 2 + 3j # complex number
s = "Python" # string
flag = True # boolean
4.5 Dynamic Typing
Python allows you to change the type of a variable by assigning a new value.
x = 10 # integer
print(type(x))
x = "Hello" # string
print(type(x))
4.6 Global and Local Variables
A variable declared inside a function is local, and outside is global.
x = "global"
def my_func():
x = "local"
print("Inside:", x)
my_func()
print("Outside:", x)
4.7 The global Keyword
Use global keyword to modify a global variable inside a function.
x = "awesome"
def change_global():
global x
x = "fantastic"
change_global()
print("Python is", x)
4.8 Deleting Variables
Use del to delete a variable.
x = 50
print(x)
del x
# print(x) # This will cause an error because x is deleted
4.9 Constants in Python
Python doesn’t have real constants, but by convention, variables written in uppercase are treated as constants.
PI = 3.14159
GRAVITY = 9.8
print(PI)
print(GRAVITY)
4.10 Best Practices for Variables
- Use meaningful names (
ageis better thana). - Follow snake_case convention (e.g.,
student_name). - Use uppercase for constants.
- Avoid single-letter names except for counters (i, j, k).
5. Data Types in Python
Data types tell Python what kind of value a variable holds. Python is dynamically typed, meaning you don’t need to declare the data type explicitly — Python assigns it based on the value.
5.1 Basic Data Types
- int: Integer numbers
- float: Decimal numbers
- complex: Numbers with real and imaginary parts
- str: Text (string)
- bool: Boolean values (True/False)
- NoneType: Represents absence of value
x = 10 # int
y = 3.14 # float
z = 2 + 5j # complex
s = "Python" # string
b = True # boolean
n = None # NoneType
print(type(x))
print(type(y))
print(type(z))
print(type(s))
print(type(b))
print(type(n))
5.2 Numeric Types
Python supports three numeric types: int, float, and complex.
a = 5 # int
b = 2.7 # float
c = 3 + 4j # complex
print(a + b)
print(c.real) # real part
print(c.imag) # imaginary part
5.3 Strings
Strings are sequences of characters enclosed in quotes.
text1 = 'Hello'
text2 = "World"
text3 = """This is
a multi-line string"""
print(text1, text2)
print(text3)
String Operations
name = "Python"
print(len(name)) # length
print(name.upper()) # uppercase
print(name.lower()) # lowercase
print(name[0]) # indexing
print(name[0:3]) # slicing
print("Py" in name) # membership
5.4 Boolean Type
Boolean values represent truth (True or False).
x = 5
y = 10
print(x > y) # False
print(x < y) # True
print(bool("Hello")) # True (non-empty string)
print(bool("")) # False (empty string)
5.5 None Type
None represents the absence of a value.
value = None
print(value)
print(type(value))
5.6 Collection Data Types
Python provides powerful built-in collections to store multiple items.
- list: Ordered, mutable collection
- tuple: Ordered, immutable collection
- set: Unordered, unique elements
- dict: Key-value pairs
Lists
fruits = ["apple", "banana", "cherry"]
print(fruits)
fruits.append("mango")
print(fruits[1])
Tuples
colors = ("red", "green", "blue")
print(colors)
print(colors[0])
Sets
numbers = {1, 2, 3, 3, 4}
print(numbers) # duplicates are removed
numbers.add(5)
print(numbers)
Dictionaries
student = {"name": "Amit", "age": 20, "grade": "A"}
print(student["name"])
student["age"] = 21
print(student)
5.7 Type Conversion
You can convert between data types using built-in functions.
x = 10
y = float(x) # int → float
z = str(x) # int → string
a = int("25") # string → int
print(y, type(y))
print(z, type(z))
print(a, type(a))
5.8 Checking Data Type
Use type() or isinstance() to check a variable’s type.
x = [1, 2, 3]
print(type(x))
print(isinstance(x, list))
5.9 Summary Table
| Type | Description | Example |
|---|---|---|
| int | Integer numbers | 10, -5 |
| float | Decimal numbers | 3.14, -0.5 |
| complex | Numbers with real and imaginary part | 2+3j |
| str | String (text) | "Hello" |
| bool | Boolean (True/False) | True |
| list | Ordered, mutable collection | [1, 2, 3] |
| tuple | Ordered, immutable collection | (1, 2, 3) |
| set | Unordered, unique collection | {1, 2, 3} |
| dict | Key-value pairs | {"a":1, "b":2} |
| NoneType | Represents no value | None |
6. Operators in Python
Operators are special symbols that perform computations or operations on values and variables. Python provides a rich set of operators grouped by purpose — arithmetic, assignment, comparison, logical, bitwise, membership, identity and more. This section explains each category, shows examples, and highlights common pitfalls and best practices.
6.1 Operator categories (quick overview)
- Arithmetic:
+,-,*,/,//,%,** - Assignment:
=, augmented:+=,-=,*=, etc. - Comparison:
==,!=,<,>,<=,>= - Logical:
and,or,not - Bitwise:
&,|,^,~,<<,>> - Membership:
in,not in - Identity:
is,is not - Special / ternary:
a if cond else b
6.2 Arithmetic operators
Used for numeric calculations.
# addition, subtraction, multiplication, division
a = 7 + 3 # 10
b = 7 - 3 # 4
c = 7 * 3 # 21
d = 7 / 3 # 2.3333333333333335 (always float in Python 3)
# floor division (quotient rounded down)
e = 7 // 3 # 2
f = -7 // 3 # -3 (floor: rounds toward -infinity)
# modulo (remainder)
r = 7 % 3 # 1
# note for negatives: -7 % 3 == 2 (result has same sign as divisor)
# exponentiation
p = 2 ** 3 # 8
Notes: floor division (//) behaves differently with negative numbers (it floors), and modulo with negatives follows the rule that a == (a // b) * b + (a % b). For floating-point arithmetic be aware of precision issues (see pitfalls).
6.3 Assignment and augmented assignment
# simple assignment
x = 10
# multiple and chained assignment
a = b = c = 0
x, y = 1, 2 # tuple unpacking
# augmented assignment
x += 5 # same as x = x + 5
x *= 2 # same as x = x * 2
Important: For mutable objects augmented assignment may mutate in-place (see section 6.10).
6.4 Comparison operators
1 == 1 # True
1 != 2 # True
3 > 2 # True
2 <= 5 # True
# Chained comparisons are allowed
0 < x <= 10 # equivalent to (0 < x) and (x <= 10)
Equality vs identity: == checks equality (calls __eq__), while is checks object identity (same object in memory). Use is for singletons (e.g., None), but not for value equality.
a = [1,2,3]
b = [1,2,3]
a == b # True (same contents)
a is b # False (different objects)
6.5 Logical operators (and, or, not)
These operate on truthiness and short-circuit.
True and False # False
True or False # True
not True # False
# short-circuit behavior:
def side_effect():
print("called")
return True
False and side_effect() # side_effect() not called (short-circuit)
True or side_effect() # side_effect() not called (short-circuit)
Return values: and/or return the actual operand (not always boolean). This is useful for idioms like value = user_input or default.
"" or "fallback" # "fallback"
"hello" and "world" # "world" (first falsy or last truthy)
6.6 Bitwise operators
Operate on binary representations (integers only).
# bitwise AND, OR, XOR
5 & 3 # 1 (0b101 & 0b011 = 0b001)
5 | 3 # 7 (0b101 | 0b011 = 0b111)
5 ^ 3 # 6 (0b101 ^ 0b011 = 0b110)
# bitwise NOT (two's complement representation)
~5 # -6
# shifts
1 << 3 # 8 (multiply by 2**3)
8 >> 2 # 2 (floor divide by 2**2)
Use bin(x) to inspect binary form: bin(5) == '0b101'.
6.7 Membership operators: in / not in
"a" in "apple" # True
3 in [1,2,3] # True
"key" in {"key": 1} # True (checks keys in dictionaries)
4 not in (1,2,3) # True
6.8 Identity operators: is / is not
is checks whether two references point to the same object (identity). For checking None use is None not == None.
a = None
a is None # True
# caution: small ints and short strings may be interned by Python,
# so 'is' might appear to work for them but it's not the correct test:
x = 256
y = 256
x is y # True on many implementations (interned)
x == y # True
6.9 Operator precedence (important)
Operators have an order of evaluation. Use parentheses to make intentions explicit. Below is a compact precedence list (higher means evaluated first):
**(exponentiation, right-to-left)- Unary
+x, -x, ~x *,/,//,%+,-<<,>>&^|- Comparisons (
==, !=, <, >, <=, >=, is, in) notandor- Assignment operators (
=, +=, ...)
When in doubt, add parentheses:
result = (a + b) * c
6.10 Augmented assignment & mutability (subtle behaviour)
Augmented assignment (+=, *=, ...) may modify mutable objects in-place, while regular assignment creates a new object.
lst = [1,2]
id_before = id(lst)
lst += [3] # modifies list in-place
id_after = id(lst)
id_before == id_after # True
lst2 = [1,2]
lst2 = lst2 + [3] # creates new list object
# id(lst2) will be different
Takeaway: when using augmented assignment with mutables (lists, dicts, sets) be aware that the object may be changed in-place which affects other references to it.
6.11 Operator overloading (custom behaviour)
Python classes can define special methods (dunder methods) to customize operator behaviour: __add__, __eq__, __lt__, etc.
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1,2)
v2 = Vector(3,4)
print(v1 + v2) # Vector(4, 6)
6.12 Ternary (conditional) operator
status = "adult" if age >= 18 else "minor"
This is a concise alternative to a short if/else.
6.13 The operator module
For functional-style code you can use the operator module (handy as key functions for sorting, etc.).
import operator
pairs = [(1, 'one'), (3, 'three'), (2, 'two')]
# sort by first item (index 0)
pairs.sort(key=operator.itemgetter(0))
# function equivalent
pairs.sort(key=lambda x: x[0])
6.14 Practical examples & common pitfalls
- Float precision:
0.1 + 0.2 != 0.3due to binary floating-point. Usemath.isclose()for comparisons. - Division:
/always returns float. Use//for integer (floor) division. isvs==: Use==for value equality andisonly when you need identity (e.g.,is None).- Membership performance:
x in listis O(n), butx in set/x in dictis on average O(1). - Bitwise vs logical: Bitwise operators operate on integers (or booleans seen as ints); do not confuse them with logical operators (
&vsand). - Chained comparisons:
1 < x < 5is elegant and efficient — it does not evaluatextwice.
6.15 Quick cheatsheet
| Operator | Meaning | Example |
|---|---|---|
+ | Add | 2 + 3 # 5 |
- | Subtract / unary minus | -5 |
* | Multiply / repeat (sequences) | [1]*3 # [1,1,1] |
/ | True division | 5 / 2 # 2.5 |
// | Floor division | 5 // 2 # 2 |
% | Modulo | 7 % 3 # 1 |
** | Exponent | 2 ** 3 # 8 |
==, != | Equality / inequality | 1 == 1 # True |
&, |, ^ | Bitwise AND/OR/XOR | 5 & 3 # 1 |
and, or, not | Logical | True and False # False |
in, not in | Membership | 'a' in 'apple' # True |
is, is not | Identity | a is None |
6.16 Summary
Operators are fundamental. Learn the categories, understand precedence, be aware of mutability and short-circuiting, and always prefer readable expressions with parentheses where there is any risk of ambiguity. For custom types implement the appropriate dunder methods to get intuitive operator behaviour.
7. Input & Output (I/O) in Python — full details
Input/Output (I/O) covers how your program receives data and how it shows results. This section focuses on console/terminal I/O (keyboard & screen), standard streams, formatting output, reading from pipes/STDIN, and best practices for interactive and script use.
7.1 The basic print()
print() is the primary way to send text to standard output.
print("Hello, world!") # simple
# print multiple values (space-separated by default)
x, y = 10, 20
print("x =", x, "y =", y)
# change separator and line end
print("a", "b", "c", sep="|") # a|b|c
print("No newline", end="") # suppress newline
print(" next") # continues same line
7.2 Advanced print() options
# print to a different file-like object (stderr)
import sys
print("This is an error", file=sys.stderr)
# flush output immediately (useful for progress updates)
print("Processing...", flush=True)
7.3 Formatted output — three common approaches
Formatting makes output readable and lets you control numeric precision, padding and alignment.
Old-style (%) formatting (legacy)
name = "Amit"
score = 95.678
print("Name: %s, Score: %.2f" % (name, score))
str.format() (flexible)
print("Name: {}, Score: {:.2f}".format(name, score))
# named fields
print("Name: {n}, Score: {s:.1f}".format(n=name, s=score))
f-strings (Python 3.6+) — recommended
print(f"Name: {name}, Score: {score:.2f}")
# width, alignment, fill and thousand separator
num = 12345.6789
print(f"{num:,.2f}") # 12,345.68
print(f"{name:>10}") # right aligned in width 10
print(f"{name:*^10}") # centered width 10, '*' fill
7.4 Reading console input with input()
input(prompt) reads one line from the user (as string). It raises EOFError on EOF.
name = input("Enter your name: ")
age_str = input("Enter age: ")
try:
age = int(age_str)
except ValueError:
print("Please enter a valid integer for age")
Common patterns: splitting multiple values in one line:
# user enters: 10 20 30
a, b, c = map(int, input("Enter three numbers: ").split())
7.5 Robust input handling
import sys
try:
data = input("Enter a number: ")
except EOFError:
print("No input (EOF). Exiting.")
sys.exit(0)
except KeyboardInterrupt:
print("\nCancelled by user.")
sys.exit(1)
7.6 Standard streams: sys.stdin, sys.stdout, sys.stderr
These let you read/write programmatically and are used for piping and redirection.
import sys
# read full stdin (useful when input is piped)
data = sys.stdin.read()
# iterate lines from stdin (memory efficient)
for line in sys.stdin:
process(line)
# write directly without newline
sys.stdout.write("No newline")
sys.stdout.flush()
7.7 Binary streams
For byte-level I/O (images, compressed data) use the buffered streams.
import sys
# read bytes from stdin
data_bytes = sys.stdin.buffer.read()
# write bytes to stdout
sys.stdout.buffer.write(b"\x00\x01")
sys.stdout.buffer.flush()
7.8 Reading until EOF / piping example
Useful when your script is part of a shell pipeline.
import sys
# echo program: reads stdin, writes uppercase to stdout
for line in sys.stdin:
sys.stdout.write(line.upper())
7.9 Progress updates on same line
Use end='\r' and flush=True to overwrite the same console line.
import time, sys
for i in range(1, 6):
print(f"Progress: {i}/5", end="\r", flush=True)
time.sleep(0.5)
print() # finish with newline
7.10 Hidden input (passwords)
from getpass import getpass
pwd = getpass("Enter password: ")
# getpass does not echo characters to the terminal
7.11 Printing to files and logging
Prefer logging for production. For quick scripts you can print to a file-like object.
with open("out.log", "a", encoding="utf-8") as logfile:
print("An event occurred", file=logfile)
# or use logging module (recommended)
import logging
logging.basicConfig(level=logging.INFO)
logging.info("Started")
7.12 Interactive vs non-interactive scripts
Interactive programs use input() and expect a terminal. Non-interactive scripts should read
from sys.stdin (so they work with pipes) and avoid prompting unless sys.stdin.isatty() is True.
import sys
if sys.stdin.isatty():
name = input("Name: ")
else:
# data is coming from a pipe or file
data = sys.stdin.read()
7.13 Encoding & terminals
When printing non-ASCII text be aware of terminal encoding. Modern terminals generally support UTF-8.
If you get encoding errors, you may use sys.stdout.reconfigure(encoding="utf-8") (Py3.7+) or set environment locale.
7.14 Example: Simple CLI calculator (reads tokens)
# usage: echo "10 + 20" | python calc.py
import sys
expr = sys.stdin.read().strip()
if not expr:
print("No expression provided.")
else:
try:
# WARNING: eval can be dangerous with untrusted input.
result = eval(expr, {"__builtins__": None}, {})
print(result)
except Exception as e:
print("Error:", e, file=sys.stderr)
7.15 Common pitfalls & tips
- Don't use
evalon untrusted input — it runs arbitrary code. - Validate and
try/exceptwhen converting input (e.g.,int(),float()). - Prefer
for line in sys.stdinfor piped input — it's memory efficient. - Use
print(..., file=sys.stderr)for error messages so they don't mix with normal output streams. - Use
loggingfor traceability and different log levels (INFO, WARNING, ERROR).
7.16 Quick cheatsheet
| Task | Example |
|---|---|
| Simple print | print("hello") |
| Formatted (f-string) | print(f"Val={val:.2f}") |
| Read one line | line = input("prompt: ") |
| Read piped data | data = sys.stdin.read() |
| Write to stderr | print("err", file=sys.stderr) |
| Byte I/O | sys.stdin.buffer.read() |
| Hidden input | pwd = getpass() |
7.17 Summary
Console I/O in Python is simple but powerful. Use print() and input() for quick scripts,
prefer sys.stdin/sys.stdout for piped data, use buffering/flush control for progress displays, and
choose logging for real applications. Always validate input and be careful with operations like eval.
8. Conditional Statements in Python
Conditional statements in Python allow us to execute different blocks of code depending on whether a condition is True or False. These statements are the decision-making backbone of Python programming.
8.1 The if Statement
The if statement is used to check a condition.
If the condition evaluates to True, the indented block under if will execute.
x = 10
if x > 5:
print("x is greater than 5")
8.2 The if-else Statement
The if-else statement provides an alternative path.
If the condition is False, the else block executes.
x = 3
if x > 5:
print("x is greater than 5")
else:
print("x is less than or equal to 5")
8.3 The if-elif-else Ladder
When there are multiple conditions, we use elif
(short for "else if") between if and else.
marks = 85
if marks >= 90:
print("Grade: A")
elif marks >= 75:
print("Grade: B")
elif marks >= 50:
print("Grade: C")
else:
print("Grade: Fail")
8.4 Nested if Statements
You can place one if statement inside another.
This is known as a nested if.
age = 20
if age >= 18:
if age < 60:
print("You are an adult but not a senior citizen")
else:
print("You are a senior citizen")
else:
print("You are under 18")
8.5 Short Hand if
Python allows writing if statements in a single line.
x = 7
if x > 5: print("x is greater than 5")
8.6 Short Hand if-else (Ternary Operator)
A shorthand way to write if-else in one line.
a = 10
b = 20
print("a is greater") if a > b else print("b is greater")
8.7 Logical Operators with Conditions
Conditions can be combined using and, or, and not.
x = 15
if x > 10 and x < 20:
print("x is between 10 and 20")
x = 5
if x < 10 or x == 20:
print("Condition is True")
x = False
if not x:
print("x is False")
8.8 Using Conditions with Membership Operators
Membership operators (in, not in)
are useful for checking values inside sequences.
fruits = ["apple", "banana", "cherry"]
if "banana" in fruits:
print("Banana is available")
if "mango" not in fruits:
print("Mango is not available")
8.9 Using Conditions with Identity Operators
Identity operators (is, is not)
check whether two variables refer to the same object.
a = [1, 2, 3]
b = a
c = [1, 2, 3]
print(a is b) # True (same object)
print(a is c) # False (different objects with same values)
print(a is not c) # True
8.10 Example: Even or Odd
num = 7
if num % 2 == 0:
print("Even number")
else:
print("Odd number")
8.11 Example: Checking Leap Year
year = 2024
if (year % 400 == 0) or (year % 4 == 0 and year % 100 != 0):
print("Leap Year")
else:
print("Not a Leap Year")
8.12 Summary of Conditional Statements
| Statement | Usage | Example |
|---|---|---|
| if | Executes if condition is True | if x > 5: |
| if-else | Chooses between two paths | if x>5 else |
| if-elif-else | Multiple conditions | elif x==10: |
| nested if | if inside another if | if a>0: if a%2==0: |
| short hand if | One line if | if x>5: print() |
| ternary operator | Short if-else | print("A") if a>b else print("B") |
9. Loops in Python
Loops in Python allow us to repeat a block of code multiple times. They are essential when you want to perform repetitive tasks efficiently. Python mainly provides two types of loops: for loop and while loop.
9.1 The for Loop
A for loop is used to iterate over a sequence such as a list, tuple, dictionary, set, or string.
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
9.2 The range() Function
The range() function is commonly used with for loops
to generate a sequence of numbers.
for i in range(5):
print(i) # prints 0 to 4
for i in range(2, 10, 2):
print(i) # prints even numbers 2, 4, 6, 8
9.3 The while Loop
A while loop executes as long as a given condition is True.
x = 1
while x <= 5:
print(x)
x += 1
9.4 The break Statement
The break statement terminates the loop immediately,
even if the condition is still True.
for i in range(10):
if i == 5:
break
print(i)
9.5 The continue Statement
The continue statement skips the current iteration
and moves to the next iteration of the loop.
for i in range(6):
if i == 3:
continue
print(i)
9.6 The else Block with Loops
A loop can have an else block. The else part executes
only if the loop is not terminated by a break.
for i in range(5):
print(i)
else:
print("Loop finished!")
9.7 Nested Loops
You can place one loop inside another. This is useful for working with matrices or patterns.
for i in range(3): # outer loop
for j in range(2): # inner loop
print(f"i={i}, j={j}")
9.8 Looping Through a String
for ch in "Python":
print(ch)
9.9 Looping Through a Dictionary
student = {"name": "Amit", "age": 21, "course": "Python"}
for key, value in student.items():
print(key, ":", value)
9.10 Infinite Loops
A loop that never ends is called an infinite loop.
Use with caution and always include a break condition.
while True:
print("This is an infinite loop")
break
9.11 Common Loop Patterns
- Summing numbers:
total = 0
for i in range(1, 6):
total += i
print("Sum:", total)
num = 5
fact = 1
while num > 0:
fact *= num
num -= 1
print("Factorial:", fact)
9.12 Practical Example: Multiplication Table
num = 7
for i in range(1, 11):
print(f"{num} x {i} = {num * i}")
9.13 Summary of Loops
| Concept | Description | Example |
|---|---|---|
| for loop | Iterates over a sequence | for i in range(5): |
| while loop | Repeats while condition is True | while x <= 5: |
| break | Stops the loop immediately | if i==5: break |
| continue | Skips current iteration | if i==3: continue |
| else with loop | Executes if loop ends normally | for i in range(3): ... else: |
| nested loops | Loop inside another loop | for i in ... for j in ... |
10. Functions in Python
A function in Python is a block of organized, reusable code that performs a specific task. Functions help make code modular, easier to read, maintain, and debug.
10.1 Defining a Function
A function is defined using the def keyword followed by the function name,
parentheses, and a colon. Inside the block, we write the function body.
def greet():
print("Hello, welcome to Python!")
10.2 Calling a Function
To execute a function, simply use its name followed by parentheses.
greet()
10.3 Function with Parameters
Parameters allow us to pass values into a function. These values are called arguments when passed during the call.
def greet(name):
print(f"Hello, {name}!")
greet("Amit")
greet("Priya")
10.4 Function with Return Value
Functions can return values using the return statement.
def add(a, b):
return a + b
result = add(5, 3)
print("Sum:", result)
10.5 Default Parameters
You can assign default values to parameters. If no argument is provided, the default is used.
def greet(name="Guest"):
print(f"Hello, {name}!")
greet()
greet("Suman")
10.6 Keyword Arguments
Python allows specifying arguments using parameter names.
def introduce(name, age):
print(f"My name is {name} and I am {age} years old.")
introduce(age=25, name="Rahul")
10.7 Variable-Length Arguments
Sometimes, you may not know in advance how many arguments will be passed. Python supports two types:
- *args → Non-keyword variable arguments
- **kwargs → Keyword variable arguments
# Using *args
def add_numbers(*args):
return sum(args)
print(add_numbers(2, 4, 6, 8))
# Using **kwargs
def display_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
display_info(name="Ankit", age=30, city="Delhi")
10.8 Nested Functions
You can define a function inside another function.
def outer():
print("This is outer function.")
def inner():
print("This is inner function.")
inner()
outer()
10.9 Lambda Functions (Anonymous Functions)
Lambda functions are small, anonymous functions defined with the lambda keyword.
square = lambda x: x * x
print(square(5))
add = lambda a, b: a + b
print(add(3, 7))
10.10 Recursion
A function that calls itself is called a recursive function.
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
print("Factorial of 5:", factorial(5))
10.11 The pass Statement
If a function has no implementation yet, you can use pass as a placeholder.
def future_function():
pass
10.12 Docstrings
Functions can have documentation strings (docstrings) to describe what the function does.
def greet(name):
"""This function greets the person passed as a parameter."""
print("Hello,", name)
print(greet.__doc__)
10.13 Built-in Functions vs User-Defined Functions
Python provides many built-in functions like len(), print(), type(), etc.
User-defined functions are the ones you create yourself.
10.14 Practical Example: Calculator Function
def calculator(a, b, operation):
if operation == "add":
return a + b
elif operation == "subtract":
return a - b
elif operation == "multiply":
return a * b
elif operation == "divide":
return a / b
else:
return "Invalid operation"
print(calculator(10, 5, "add"))
print(calculator(10, 5, "multiply"))
10.15 Summary of Functions
| Concept | Description | Example |
|---|---|---|
| Defining | Create function with def | def greet(): |
| Calling | Execute function | greet() |
| Parameters | Pass values to function | def add(a,b): |
| Return | Send back a value | return a+b |
| *args | Variable non-keyword args | def f(*args): |
| **kwargs | Variable keyword args | def f(**kwargs): |
| Lambda | Anonymous small function | lambda x: x*x |
| Recursion | Function calling itself | factorial(n) |
11. Lists in Python
A list in Python is a collection that can store multiple items in a single variable.
Lists are ordered, mutable (changeable), and allow duplicate elements.
They are defined using square brackets [].
11.1 Creating Lists
# Empty list
my_list = []
# List of integers
numbers = [1, 2, 3, 4, 5]
# Mixed data types
mixed = [10, "apple", 3.14, True]
11.2 Accessing List Elements
Use index numbers (starting from 0) to access elements.
fruits = ["apple", "banana", "cherry"]
print(fruits[0]) # apple
print(fruits[2]) # cherry
11.3 Negative Indexing
Negative indices count from the end (-1 is last element).
fruits = ["apple", "banana", "cherry"]
print(fruits[-1]) # cherry
print(fruits[-2]) # banana
11.4 List Slicing
Use slicing to get a sub-list.
fruits = ["apple", "banana", "cherry", "date", "mango"]
print(fruits[1:4]) # ['banana', 'cherry', 'date']
print(fruits[:3]) # ['apple', 'banana', 'cherry']
print(fruits[2:]) # ['cherry', 'date', 'mango']
11.5 Modifying List Elements
fruits = ["apple", "banana", "cherry"]
fruits[1] = "blueberry"
print(fruits) # ['apple', 'blueberry', 'cherry']
11.6 Adding Elements
append()→ Adds an item at the end.insert()→ Inserts at a specific position.extend()→ Adds elements from another list.
fruits = ["apple", "banana"]
fruits.append("cherry")
fruits.insert(1, "orange")
fruits.extend(["mango", "grape"])
print(fruits)
11.7 Removing Elements
remove()→ Removes by value.pop()→ Removes by index (default last).del→ Deletes by index.clear()→ Removes all items.
fruits = ["apple", "banana", "cherry", "date"]
fruits.remove("banana")
fruits.pop(1)
del fruits[0]
fruits.clear()
print(fruits) # []
11.8 Iterating Over a List
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
11.9 Checking Membership
fruits = ["apple", "banana", "cherry"]
if "banana" in fruits:
print("Banana is present")
11.10 Sorting and Reversing
numbers = [5, 2, 9, 1, 7]
numbers.sort()
print(numbers) # [1, 2, 5, 7, 9]
numbers.sort(reverse=True)
print(numbers) # [9, 7, 5, 2, 1]
numbers.reverse()
print(numbers) # reversed order
11.11 Copying Lists
list1 = [1, 2, 3]
list2 = list1.copy()
list3 = list(list1)
print(list2, list3)
11.12 List Comprehensions
List comprehensions provide a concise way to create lists using a single line of code.
numbers = [x for x in range(10)]
print(numbers)
squares = [x*x for x in range(6)]
print(squares)
11.13 Nested Lists
Lists can contain other lists.
matrix = [[1, 2], [3, 4], [5, 6]]
print(matrix[1][0]) # 3
11.14 Useful List Functions
numbers = [10, 20, 30, 40]
print(len(numbers)) # length
print(max(numbers)) # maximum
print(min(numbers)) # minimum
print(sum(numbers)) # sum
11.15 Practical Example: Removing Duplicates
numbers = [1, 2, 2, 3, 4, 4, 5]
unique = list(set(numbers))
print(unique)
11.16 Summary of Lists
| Operation | Description | Example |
|---|---|---|
| append() | Add at end | fruits.append("mango") |
| insert() | Add at position | fruits.insert(1, "orange") |
| extend() | Add multiple items | fruits.extend(["grape","melon"]) |
| remove() | Remove by value | fruits.remove("apple") |
| pop() | Remove by index | fruits.pop(2) |
| clear() | Remove all items | fruits.clear() |
| sort() | Sort list | numbers.sort() |
| reverse() | Reverse list | numbers.reverse() |
| copy() | Copy list | new_list = old.copy() |
| comprehension | Quick list creation | [x*x for x in range(5)] |
12. Tuples in Python (Full details)
A tuple is an ordered, immutable sequence type in Python. Tuples are similar to lists but cannot be changed after creation (no item assignment, no append/pop). Because of immutability, tuples are often used for fixed collections of items (like coordinates), as dictionary keys, or to return multiple values from functions.
12.1 Creating tuples
# using parentheses
t1 = (1, 2, 3)
# parentheses are optional for simple packing
t2 = 4, 5, 6
# empty tuple
t0 = ()
# single-element tuple (note trailing comma)
single = (5,)
not_a_tuple = (5) # this is just integer 5
12.2 Accessing elements (indexing & slicing)
t = ("a", "b", "c", "d")
print(t[0]) # 'a'
print(t[-1]) # 'd' (last element)
# slicing returns a new tuple
print(t[1:3]) # ('b', 'c')
print(t[:2]) # ('a', 'b')
12.3 Tuple immutability (what you can and cannot do)
Once created, elements cannot be replaced or removed using list-style methods. You can, however, create new tuples by concatenation or conversion.
t = (1, 2, 3)
# invalid: t[0] = 10 # TypeError
# to 'change' create a new tuple
t = t + (4,) # (1, 2, 3, 4)
t = (0,) + t # (0, 1, 2, 3, 4)
12.4 Single-element tuple gotcha
a = (10) # int
b = (10,) # tuple with one element
print(type(a), type(b))
12.5 Tuple methods
Tuples have only a few methods because they're immutable.
t = (1,2,2,3)
print(t.count(2)) # 2 (number of occurrences)
print(t.index(3)) # 3 (index of first occurrence)
12.6 Packing and unpacking
Packing packs multiple values into a tuple; unpacking assigns tuple elements to variables.
# packing
pair = ("John", 30)
# unpacking
name, age = pair
print(name) # John
print(age) # 30
# swapping variables (idiomatic Python)
a, b = 1, 2
a, b = b, a
12.7 Extended unpacking (starred expressions)
nums = (1,2,3,4,5)
first, *middle, last = nums
print(first) # 1
print(middle) # [2,3,4] (note: starred part becomes a list)
print(last) # 5
12.8 Returning multiple values from functions
Functions often return tuples implicitly — convenient for multiple results.
def min_max(seq):
return min(seq), max(seq)
mn, mx = min_max([10, 2, 9, 4])
print(mn, mx) # 2 9
12.9 Iteration & common patterns
t = (10, 20, 30)
for item in t:
print(item)
# enumerating
for idx, val in enumerate(t):
print(idx, val)
12.10 Converting between tuples and lists
lst = [1, 2, 3]
t = tuple(lst) # (1, 2, 3)
t2 = (4, 5)
lst2 = list(t2) # [4, 5]
12.11 Concatenation and repetition
(1,2) + (3,4) # (1, 2, 3, 4)
(0,) * 3 # (0, 0, 0)
12.12 Tuples as dictionary keys and set elements
Tuples (containing only hashable items) are hashable themselves and can be used as keys.
coords = (10, 20)
d = { coords: "Location A" }
print(d[(10, 20)])
12.13 Immutability caveat: mutable elements inside a tuple
A tuple is immutable, but it may contain mutable objects which can be changed.
t = ([1,2], 3)
t[0].append(4) # allowed — modifies the list inside the tuple
print(t) # ([1, 2, 4], 3)
12.14 Memory & performance notes
Tuples typically use slightly less memory than lists and are marginally faster for iteration when values are fixed. They are a good choice for heterogeneous, read-only data. For large mutable sequences prefer lists.
import sys
l = [1,2,3,4,5]
t = (1,2,3,4,5)
print(sys.getsizeof(l), sys.getsizeof(t)) # sizes may differ by implementation
12.15 Named tuples (readability & immutability)
The collections.namedtuple creates tuple-like objects with named fields.
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(10, 20)
print(p.x, p.y) # 10 20
print(isinstance(p, tuple)) # True
12.16 Using tuple() constructor and generator expressions
t = tuple([x*x for x in range(5)]) # from list comprehension
t2 = tuple(x*x for x in range(5)) # from generator expression
12.17 Comparison of tuple vs list (when to use)
- Use tuple for heterogeneous, fixed-size, or read-only collections (e.g., coordinates, records).
- Use list when you need to modify the collection (add/remove/replace items).
- Tuples can be dictionary keys — lists cannot.
12.18 Practical examples
# Function returning multiple results
def divide_and_remainder(a, b):
return a // b, a % b
quot, rem = divide_and_remainder(17, 5)
print(quot, rem) # 3 2
# Use tuple for constant configuration
DB_CONFIG = ("db.example.com", 5432, "user", "pass")
12.19 Common pitfalls & tips
- Remember the trailing comma for single-element tuple:
(42,). - Do not expect tuple methods like
append()orpop()— they don't exist. - If you need to change many items, convert to a list, mutate, then convert back:
t = tuple(list(t)). - Be careful when tuples contain mutable objects — their contents can still change.
12.20 Quick cheatsheet
| Operation | Example |
|---|---|
| Create | t = (1,2,3) or t = 1,2,3 |
| Single element | t = (5,) |
| Unpack | a,b = (1,2) |
| Slice | t[1:3] |
| Concat / repeat | t + (4,) ; t * 3 |
| Convert | tuple(list) ; list(tuple) |
| Methods | count(), index() |
12.21 Summary
Tuples are lightweight, immutable sequences ideal for fixed collections and keys in mappings. They support packing/unpacking, slicing, and have minimal methods. Use them when immutability, small memory footprint, or hashability is desired.
13. Sets in Python (Full Details)
A set in Python is an unordered, mutable collection of unique elements. Sets are useful when you want to store multiple items without duplicates and perform mathematical set operations like union, intersection, and difference.
13.1 Creating Sets
# empty set (must use set(), not {})
s = set()
# from a list
s1 = set([1, 2, 3, 4, 4]) # {1, 2, 3, 4}
# directly using curly braces
s2 = {1, 2, 3, 4}
# mixed data types
s3 = {10, "apple", 3.14, True}
print(s1, s2, s3)
13.2 Properties of Sets
- Unordered → no indexing or slicing allowed.
- Unique → duplicate elements are automatically removed.
- Mutable → items can be added or removed, but only immutable items (numbers, strings, tuples) can be elements.
13.3 Adding Elements
add()→ Add a single item.update()→ Add multiple items (from list, set, tuple).
fruits = {"apple", "banana"}
fruits.add("cherry")
fruits.update(["mango", "grape"])
print(fruits)
13.4 Removing Elements
remove()→ Removes item; error if not present.discard()→ Removes item; no error if not present.pop()→ Removes and returns a random item.clear()→ Empties the set.
fruits = {"apple", "banana", "cherry"}
fruits.remove("banana")
fruits.discard("pear") # no error
print(fruits)
item = fruits.pop()
print("Removed:", item)
fruits.clear()
print(fruits) # set()
13.5 Iterating Over a Set
fruits = {"apple", "banana", "cherry"}
for f in fruits:
print(f)
13.6 Membership Test
fruits = {"apple", "banana", "cherry"}
print("apple" in fruits) # True
print("mango" not in fruits) # True
13.7 Set Operations (Mathematical)
Python sets support union, intersection, difference, and symmetric difference.
A = {1, 2, 3, 4}
B = {3, 4, 5, 6}
print(A | B) # Union → {1,2,3,4,5,6}
print(A & B) # Intersection → {3,4}
print(A - B) # Difference → {1,2}
print(B - A) # Difference → {5,6}
print(A ^ B) # Symmetric difference → {1,2,5,6}
13.8 Set Methods for Operations
A = {1,2,3}
B = {3,4,5}
print(A.union(B))
print(A.intersection(B))
print(A.difference(B))
print(A.symmetric_difference(B))
13.9 Subset and Superset
A = {1,2,3}
B = {1,2,3,4,5}
print(A.issubset(B)) # True
print(B.issuperset(A)) # True
13.10 Disjoint Sets
Two sets are disjoint if they have no elements in common.
A = {1,2,3}
B = {4,5,6}
print(A.isdisjoint(B)) # True
13.11 Frozensets (Immutable Sets)
A frozenset is an immutable version of a set — elements cannot be added or removed.
A = frozenset([1,2,3,4])
print(A)
# frozensets can be used as dictionary keys
d = {A: "numbers"}
print(d)
13.12 Set Comprehensions
squares = {x*x for x in range(6)}
print(squares) # {0, 1, 4, 9, 16, 25}
13.13 Removing Duplicates from a List using Set
numbers = [1,2,2,3,4,4,5]
unique = list(set(numbers))
print(unique)
13.14 Practical Examples
# Finding unique words in a sentence
sentence = "python is great and python is easy"
words = set(sentence.split())
print(words)
# Common friends example
friends_A = {"John", "Alice", "Bob"}
friends_B = {"Alice", "David", "Bob"}
common = friends_A & friends_B
print("Common friends:", common)
13.15 Common Pitfalls
- Empty set must be created with
set(), not{}(which creates a dictionary). - Sets are unordered, so you cannot index them (e.g.,
set[0]→ error). - Only immutable elements (numbers, strings, tuples) can be set members. Lists and dictionaries cannot.
13.16 Useful Built-in Functions with Sets
A = {1,2,3,4,5}
print(len(A)) # 5
print(max(A)) # 5
print(min(A)) # 1
print(sum(A)) # 15
13.17 Performance Notes
Sets use hash tables internally, so membership tests (in and not in) are much faster than lists, especially for large collections.
13.18 Quick Cheatsheet
| Operation | Example |
|---|---|
| Create | {1,2,3} or set([1,2,3]) |
| Add | s.add(4) |
| Update | s.update([5,6]) |
| Remove | s.remove(2) |
| Discard | s.discard(10) |
| Union | A | B |
| Intersection | A & B |
| Difference | A - B |
| Symmetric Difference | A ^ B |
| Subset | A.issubset(B) |
| Superset | A.issuperset(B) |
| Disjoint | A.isdisjoint(B) |
13.19 Summary
Sets are powerful for handling unique elements and performing mathematical operations efficiently. Use them when you need fast membership tests, unique collections, or set algebra operations.
14. Dictionaries in Python (Full details)
A dictionary (or dict) is Python’s built-in mapping type: an unordered (in older versions) — now insertion-ordered — collection of key:value pairs.
Dictionaries are mutable, fast for lookups (average O(1)), and extremely useful for representing structured data.
14.1 Creating dictionaries
# literal syntax
d1 = {"name": "Amit", "age": 30, "city": "Delhi"}
# using dict() constructor
d2 = dict(name="Rina", age=25)
# from pairs (list of tuples)
d3 = dict([("a", 1), ("b", 2)])
# empty dict
empty = {}
14.2 Accessing values
Access by key using square brackets or get() which is safer for missing keys.
print(d1["name"]) # Amit
# safer access (returns None if missing)
print(d1.get("salary")) # None
# with default
print(d1.get("salary", 0)) # 0
14.3 Adding & updating entries
d = {}
d["name"] = "Asha" # add
d["age"] = 28 # add
d["age"] = 29 # update
# update many at once
d.update({"city": "Mumbai", "role": "engineer"})
14.4 Removing entries
14.5 Iterating dictionaries
Common iteration patterns:
for key in d: # iterates keys
print(key, d[key])
for key in d.keys(): # explicit keys view
print(key)
for value in d.values(): # values view
print(value)
for key, value in d.items(): # key-value pairs
print(key, value)
Note: keys(), values(), and items() return dynamic view objects — they reflect changes to the dict.
14.6 Membership tests
"name" in d # True if key exists
"value" in d.values() # checks values (less efficient)
14.7 Dictionary comprehensions
14.8 Common useful methods
d.get(key, default)— safe accessd.setdefault(key, default)— get existing or set default and return itd.update(other)— merge/updated.pop(key[, default])— remove and return valued.popitem()— remove and return (key, value)d.clear()— remove all itemsd.copy()— shallow copy
# setdefault example (useful for grouping)
words = ["apple", "banana", "apple", "orange"]
counts = {}
for w in words:
counts.setdefault(w, 0)
counts[w] += 1
# using get (more concise)
counts2 = {}
for w in words:
counts2[w] = counts2.get(w, 0) + 1
14.9 Merging dictionaries
Multiple ways to merge dicts:
# Python 3.9+: | operator (returns new dict)
d3 = d1 | d2
# Python 3.5+ unpacking
d4 = {**d1, **d2} # keys from d2 override d1
# in-place merge
d1.update(d2) # modifies d1
14.10 Shallow vs Deep copy
Shallow copy copies the mapping structure but not nested mutable objects. Use copy.deepcopy for deep copying.
import copy
orig = {"a": [1,2], "b": {"x": 10}}
shallow = orig.copy()
deep = copy.deepcopy(orig)
orig["a"].append(3)
print(shallow["a"]) # changed too (shares same list)
print(deep["a"]) # unaffected
14.11 Using immutable keys
Dictionary keys must be hashable (immutable types like strings, numbers, tuples of immutables). Lists and dicts cannot be keys.
valid = {(1,2): "coord", "name": "Amit", 100: "id"}
# invalid: {[1,2]: "no"} # TypeError: unhashable type: 'list'
14.12 Ordering guarantees
From Python 3.7+, the insertion order of keys is guaranteed. That means iteration follows insertion order (useful for deterministic behavior).
14.13 Nested dictionaries
data = {
"user1": {"name": "A", "age": 30},
"user2": {"name": "B", "age": 25}
}
# access nested value
print(data["user1"]["name"])
14.14 defaultdict and Counter (collections)
For common patterns use helpers from collections:
from collections import defaultdict, Counter
# defaultdict avoids manual setdefault/get patterns
dd = defaultdict(list)
dd["a"].append(1)
# Counter is great for counting items
cnt = Counter(["a", "b", "a"])
print(cnt) # Counter({'a': 2, 'b': 1})
14.15 OrderedDict (legacy use)
collections.OrderedDict preserves insertion order in older Python versions (<3 .7="" 3.7="" behavior="" dict="" for="" is="" mainly="" move_to_end="" needed="" normal="" order="" ordereddict="" p="" preserves="" reordering="" since="" specialised="">
3>
from collections import OrderedDict
od = OrderedDict()
od["a"] = 1
od["b"] = 2
od.move_to_end("a") # move 'a' to the end
14.16 JSON and dicts
Dictionaries map naturally to JSON objects. Use json.dump/json.load for IO.
import json
with open("data.json", "w", encoding="utf-8") as f:
json.dump(d1, f, ensure_ascii=False, indent=2)
with open("data.json", "r", encoding="utf-8") as f:
d_loaded = json.load(f)
14.17 Sorting dictionaries
Dicts themselves are unordered containers of key:value, but you can create sorted views or new dicts ordered by key or value:
# sort by keys
for k in sorted(d.keys()):
print(k, d[k])
# sort by values (descending) and create ordered dict
sorted_by_value = dict(sorted(d.items(), key=lambda kv: kv[1], reverse=True))
14.18 Performance notes
- Average lookup, insertion, and deletion are O(1).
- Be mindful of memory: dicts have overhead (hash table).
- Use appropriate key types (immutable, small) for best performance.
14.19 Practical examples
Counting words (Counter)
from collections import Counter
text = "this is a sample this is a test"
words = text.split()
counts = Counter(words)
print(counts.most_common(3))
Group items by key
items = [("fruit","apple"), ("veg","carrot"), ("fruit","banana")]
grouped = {}
for k, v in items:
grouped.setdefault(k, []).append(v)
print(grouped)
14.20 Common pitfalls & tips
- Don’t use mutable objects (lists, dicts) as keys — they’re unhashable.
- Use
get()to avoidKeyError. - When merging dicts, be clear whether you want a new dict or to update in-place.
- Remember shallow copy semantics when dict values are mutable.
- Prefer
Counter/defaultdictfor counting & grouping tasks — less boilerplate and faster.
14.21 Quick cheatsheet
| Task | Example |
|---|---|
| Create | d = {"a":1} |
| Access (safe) | v = d.get("a", default) |
| Set / Update | d["a"]=2 ; d.update({"b":3}) |
| Remove | d.pop("a", None) ; del d["b"] |
| Iterate | for k,v in d.items(): ... |
| Copy | d2 = d.copy() ; deep = copy.deepcopy(d) |
| Merge | d3 = d1 | d2 # py3.9+ |
| Comprehension | {k: f(k) for k in keys} |
14.22 Summary
Dictionaries are the go-to structure for mapping keys to values. Learn the common idioms:
safe access with get(), grouping with setdefault() or defaultdict,
counting with Counter, merging with | or update(), and
mind shallow vs deep copies when values are mutable. For JSON-like data, dicts and the json module are a natural fit.
15. Strings in Python (Full Details)
A string in Python is a sequence of Unicode characters used to represent text. Strings are immutable, which means once created, they cannot be changed. Any operation that modifies a string actually creates a new one.
15.1 Creating Strings
# single quotes
s1 = 'Hello'
# double quotes
s2 = "Hello World"
# triple quotes for multi-line
s3 = '''This is
a multi-line
string'''
# empty string
s4 = ""
15.2 String Indexing and Slicing
Strings are sequences, so you can access characters by index (0-based) and extract substrings with slicing.
text = "Python"
print(text[0]) # P
print(text[-1]) # n (last character)
print(text[0:4]) # Pyth (up to index 3)
print(text[:3]) # Pyt (from start)
print(text[3:]) # hon (till end)
print(text[::-1]) # nohtyP (reverse)
15.3 String Concatenation and Repetition
a = "Hello"
b = "World"
# concatenation
print(a + " " + b) # Hello World
# repetition
print(a * 3) # HelloHelloHello
15.4 String Formatting
Three main approaches exist for string formatting:
15.4.1 Old-style (%) formatting
name = "Ravi"
age = 25
print("My name is %s and I am %d years old" % (name, age))
15.4.2 str.format()
print("My name is {} and I am {} years old".format(name, age))
print("Name: {0}, Age: {1}".format(name, age))
print("Age: {age}, Name: {name}".format(name="Ravi", age=25))
15.4.3 f-strings (Python 3.6+)
print(f"My name is {name} and I am {age} years old")
print(f"Next year, age will be {age + 1}")
15.5 Useful String Methods
len(s)→ lengths.lower(),s.upper(),s.title(),s.capitalize()s.strip(),s.lstrip(),s.rstrip()s.startswith(prefix),s.endswith(suffix)s.find(sub),s.rfind(sub),s.index(sub)s.replace(old, new)s.split(sep),s.rsplit(sep),s.join(iterable)s.isalpha(),s.isdigit(),s.isalnum(),s.isspace()s.zfill(width),s.center(width, char),s.ljust(),s.rjust()
text = " Python Basics "
print(len(text)) # 16
print(text.strip()) # 'Python Basics'
print(text.lower()) # ' python basics '
print(text.upper()) # ' PYTHON BASICS '
print("abc123".isalnum()) # True
print("123".isdigit()) # True
print("abc".isalpha()) # True
15.6 Escape Characters
Escape characters allow you to include special characters inside strings.
text = "Line1\nLine2" # newline
print(text)
path = "C:\\Users\\Admin" # backslash
quote = "He said, \"Python is great!\""
raw = r"C:\Users\Admin" # raw string (ignores escapes)
15.7 String Iteration
for char in "Python":
print(char)
15.8 Membership Tests
text = "hello world"
print("hello" in text) # True
print("bye" not in text) # True
15.9 Splitting and Joining
sentence = "Python is easy to learn"
words = sentence.split() # ['Python', 'is', 'easy', 'to', 'learn']
print(words)
joined = "-".join(words) # 'Python-is-easy-to-learn'
print(joined)
15.10 Advanced: String Templates
Using string.Template for safe substitutions (useful with user input).
from string import Template
t = Template("Hello, $name!")
print(t.substitute(name="Ravi"))
15.11 Unicode and Encoding
text = "नमस्ते"
print(len(text)) # number of Unicode characters
print(text.encode("utf-8")) # b'\xe0\xa4\xa8\xe0\xa4...\xe0xa5\x87'
15.12 Common String Algorithms
Check palindrome
s = "madam"
print(s == s[::-1]) # True
Count vowels
vowels = "aeiou"
word = "education"
count = sum(1 for ch in word if ch.lower() in vowels)
print(count)
15.13 Formatting Numbers in Strings
pi = 3.14159265
print(f"{pi:.2f}") # 3.14
print(f"{1000:,}") # 1,000
15.14 String Comparisons
Strings are compared lexicographically (like dictionary order) using Unicode code points.
print("apple" < "banana") # True
print("abc" == "ABC") # False
print("abc".lower() == "ABC".lower()) # True
15.15 Summary
Strings are one of the most powerful and commonly used types in Python. Key points:
- They are immutable sequences of Unicode characters.
- Support slicing, concatenation, repetition, and iteration.
- Provide dozens of helpful methods for searching, formatting, and transforming text.
- Use f-strings for modern, clean formatting.
- Work seamlessly with Unicode and encodings.
16. File Handling in Python (Full Details)
File handling allows you to store data permanently by reading from and writing to files.
Python provides a simple and powerful interface for working with files using the built-in open() function.
Files can be text or binary, and operations include creating, reading, writing, and appending.
16.1 Opening and Closing Files
Use open(filename, mode) to work with files. Always close files with close() or preferably use the with statement which automatically closes the file.
# basic syntax
file = open("example.txt", "r") # open for reading
print(file.read())
file.close()
# recommended approach
with open("example.txt", "r") as f:
content = f.read()
print(content) # file automatically closed after block
16.2 File Modes
| Mode | Description |
|---|---|
'r' | Read (default) — file must exist |
'w' | Write — creates new or overwrites existing |
'a' | Append — creates new or writes at end |
'x' | Create — fails if file exists |
'b' | Binary mode (e.g., 'rb', 'wb') |
't' | Text mode (default) |
'+' | Read and write (e.g., 'r+', 'w+') |
16.3 Reading from Files
with open("example.txt", "r") as f:
data = f.read() # read entire file as string
print(data)
with open("example.txt", "r") as f:
line = f.readline() # read one line
print(line)
with open("example.txt", "r") as f:
lines = f.readlines() # read all lines as list
print(lines)
# iterate directly
with open("example.txt", "r") as f:
for line in f:
print(line.strip())
16.4 Writing to Files
# write (overwrite)
with open("example.txt", "w") as f:
f.write("Hello Python\n")
f.write("File Handling Example")
# append
with open("example.txt", "a") as f:
f.write("\nAdding new line")
16.5 Working with Binary Files
# write binary
with open("image.png", "rb") as f:
data = f.read()
with open("copy.png", "wb") as f:
f.write(data)
16.6 File Object Methods
f.read([size])→ read size bytes (or all)f.readline()→ read one linef.readlines()→ read all lines into listf.write(string)→ write stringf.writelines(list)→ write list of stringsf.seek(offset)→ move file pointerf.tell()→ current file pointer positionf.close()→ close file
with open("example.txt", "r") as f:
print(f.read(5)) # read first 5 characters
print(f.tell()) # pointer position
f.seek(0) # move back to start
print(f.read())
16.7 Checking File Existence
import os
print(os.path.exists("example.txt")) # True or False
16.8 Working with Directories
import os
print(os.getcwd()) # current directory
os.mkdir("testdir") # create directory
os.chdir("testdir") # change directory
os.listdir() # list files
os.rmdir("testdir") # remove directory
16.9 Exception Handling with Files
try:
with open("nofile.txt", "r") as f:
data = f.read()
except FileNotFoundError:
print("File not found")
except PermissionError:
print("No permission")
16.10 Handling CSV Files
import csv
# writing
with open("data.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["Name", "Age"])
writer.writerow(["Ravi", 25])
# reading
with open("data.csv", "r") as f:
reader = csv.reader(f)
for row in reader:
print(row)
16.11 Handling JSON Files
import json
data = {"name": "Ravi", "age": 25}
# write JSON
with open("data.json", "w") as f:
json.dump(data, f, indent=2)
# read JSON
with open("data.json", "r") as f:
loaded = json.load(f)
print(loaded)
16.12 Context Managers
with statement is best practice for file handling — it ensures files are closed automatically even in case of exceptions.
16.13 Best Practices
- Always use
with open(...)to manage files. - Use correct mode (
r,w,a, etc.). - Handle exceptions for missing files or permission errors.
- For structured data, prefer
csvorjsonmodules. - Use binary mode for non-text files (images, videos, etc.).
16.14 Summary
File handling is essential for data storage and processing. Python makes it simple with open(),
multiple file modes, and helpful modules like os, csv, and json.
Always use with for safe file operations, and apply exception handling for robustness.
17. Exception Handling in Python (Full Details)
Exceptions are errors detected during program execution. Instead of stopping the program abruptly, Python allows you to handle exceptions gracefully using try-except blocks. Exception handling makes programs robust and prevents crashes.
17.1 What is an Exception?
# Example of an exception
print(10 / 0) # ZeroDivisionError
numbers = [1, 2, 3]
print(numbers[5]) # IndexError
17.2 Basic try-except
try:
x = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero")
17.3 Multiple except blocks
try:
num = int("abc")
except ValueError:
print("Invalid number")
except ZeroDivisionError:
print("Division by zero")
17.4 Catching multiple exceptions in one block
try:
x = int("abc") / 0
except (ValueError, ZeroDivisionError) as e:
print("Error:", e)
17.5 Using else with try-except
else block runs if no exception occurs.
try:
x = int("123")
except ValueError:
print("Invalid input")
else:
print("Conversion successful:", x)
17.6 Using finally
finally block always executes (useful for cleanup like closing files).
try:
f = open("example.txt", "r")
data = f.read()
except FileNotFoundError:
print("File not found")
finally:
print("Closing file")
try:
f.close()
except:
pass
17.7 Raising exceptions manually
def divide(a, b):
if b == 0:
raise ValueError("b cannot be zero")
return a / b
try:
result = divide(5, 0)
except ValueError as e:
print("Error:", e)
17.8 Creating custom exceptions
class NegativeNumberError(Exception):
pass
def square_root(x):
if x < 0:
raise NegativeNumberError("Negative numbers not allowed")
return x ** 0.5
try:
print(square_root(-9))
except NegativeNumberError as e:
print("Custom Exception:", e)
17.9 Common built-in exceptions
ZeroDivisionError— divide by zeroValueError— invalid value (e.g., converting "abc" to int)TypeError— operation on incompatible typesIndexError— list index out of rangeKeyError— dictionary key not foundFileNotFoundError— file does not existAttributeError— invalid attribute accessImportError— module import failedStopIteration— iterator exhaustedMemoryError— out of memory
17.10 Exception hierarchy
All exceptions inherit from BaseException. Common parent is Exception.
try:
1 / 0
except Exception as e:
print("Caught:", type(e).__name__)
17.11 Best Practices
- Catch specific exceptions rather than using a blanket
except:. - Use
finallyfor cleanup (closing files, releasing resources). - Create custom exceptions for domain-specific errors.
- Don’t suppress exceptions silently — log them or re-raise when needed.
- Keep try-except blocks small and focused.
17.12 Example: File Handling with Exceptions
try:
with open("data.txt", "r") as f:
content = f.read()
except FileNotFoundError:
print("File not found")
except PermissionError:
print("No permission")
else:
print("File read successfully")
finally:
print("Execution finished")
17.13 Summary
Exception handling is essential for writing robust applications. Use try-except to catch errors,
else for successful execution, and finally for cleanup. Python’s built-in exceptions
cover most errors, but you can also define custom ones for specific needs.
18. Modules in Python (Full Details)
A module in Python is simply a file containing Python definitions and statements. Modules help you organize code logically, make it reusable, and easier to maintain. You can use built-in modules, install third-party modules, or create your own.
18.1 Why use Modules?
- Organize code into separate files for better readability.
- Reusability – write once, use anywhere.
- Avoids duplication of code.
- Provides access to Python’s huge Standard Library.
18.2 Importing a Module
import math
print(math.sqrt(25)) # 5.0
print(math.pi) # 3.141592653589793
18.3 Importing specific functions or variables
from math import sqrt, pi
print(sqrt(36)) # 6.0
print(pi)
18.4 Import with alias
import math as m
print(m.cos(0)) # 1.0
18.5 Import all symbols
Not recommended because it pollutes the namespace.
from math import *
print(sin(0))
18.6 Common Built-in Modules
- math – mathematical functions.
- random – random numbers.
- datetime – dates and times.
- os – interacting with the operating system.
- sys – system-specific parameters.
- json – JSON parsing and writing.
- re – regular expressions.
- collections – advanced data structures.
18.7 Example: Using random module
import random
print(random.randint(1, 100)) # random number between 1 and 100
print(random.choice(["apple", "banana", "cherry"]))
18.8 Example: Using datetime module
import datetime
today = datetime.date.today()
print("Today's date:", today)
print("Year:", today.year)
print("Month:", today.month)
18.9 sys module example
import sys
print("Python version:", sys.version)
print("Command line args:", sys.argv)
18.10 os module example
import os
print("Current working directory:", os.getcwd())
os.mkdir("test_folder")
18.11 Creating Your Own Module
Step 1: Create a Python file my_module.py
# my_module.py
def greet(name):
return f"Hello, {name}!"
def add(a, b):
return a + b
Step 2: Import and use it
import my_module
print(my_module.greet("Alice"))
print(my_module.add(10, 5))
18.12 Reloading Modules
If you modify a module while running Python, use importlib.reload.
import importlib
import my_module
importlib.reload(my_module)
18.13 The dir() function
Lists all functions, classes, and variables inside a module.
import math
print(dir(math))
18.14 Installing Third-party Modules
# Install using pip
pip install requests
Then use it:
import requests
response = requests.get("https://api.github.com")
print(response.status_code)
18.15 The __name__ == "__main__" check
When Python runs a file, it sets __name__ to "__main__".
This lets you write code that only runs when the file is executed directly,
not when imported.
# my_module.py
def greet():
print("Hello!")
if __name__ == "__main__":
print("Running as main script")
greet()
18.16 Summary
- Modules organize Python code.
- Import built-in modules (math, os, sys, etc.).
- Create custom modules for reusability.
- Install third-party modules with
pip. - Use
__name__ == "__main__"to control execution.
19. Classes & Object-Oriented Programming (OOP) in Python
Python is an object-oriented language, which means it allows the use of classes and objects to model real-world entities. OOP helps in organizing code, reusability, and modularity.
19.1 What is a Class and an Object?
- Class – A blueprint that defines attributes (variables) and behaviors (methods).
- Object – An instance of a class.
# Defining a class
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
def bark(self):
print(f"{self.name} is barking!")
# Creating objects
dog1 = Dog("Tommy", "Labrador")
dog2 = Dog("Rocky", "Pug")
dog1.bark()
dog2.bark()
19.2 The __init__() method
The __init__ method is a constructor that runs automatically
when a new object is created.
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
s1 = Student("Alice", 21)
print(s1.name, s1.age)
19.3 Class Attributes vs Instance Attributes
class Car:
wheels = 4 # class attribute (same for all objects)
def __init__(self, brand):
self.brand = brand # instance attribute
c1 = Car("Toyota")
c2 = Car("Honda")
print(c1.brand, c1.wheels)
print(c2.brand, c2.wheels)
19.4 Methods in Classes
- Instance Methods – Work with individual objects.
- Class Methods – Work with the class itself.
- Static Methods – Independent utility functions inside a class.
class Example:
count = 0
def __init__(self):
Example.count += 1
# Instance method
def show(self):
print("Instance method called")
# Class method
@classmethod
def total_objects(cls):
print("Total objects:", cls.count)
# Static method
@staticmethod
def greet():
print("Hello from static method")
e1 = Example()
e2 = Example()
e1.show()
Example.total_objects()
Example.greet()
19.5 Inheritance
Inheritance allows a class (child) to acquire attributes and methods from another class (parent).
class Animal:
def speak(self):
print("Animal makes a sound")
class Dog(Animal):
def speak(self):
print("Dog barks")
d = Dog()
d.speak()
19.6 Multiple Inheritance
class A:
def feature1(self):
print("Feature 1 from A")
class B:
def feature2(self):
print("Feature 2 from B")
class C(A, B):
pass
c = C()
c.feature1()
c.feature2()
19.7 super() function
Used to call parent class methods inside child class.
class Parent:
def __init__(self, name):
self.name = name
class Child(Parent):
def __init__(self, name, age):
super().__init__(name) # call parent constructor
self.age = age
c = Child("Alice", 10)
print(c.name, c.age)
19.8 Encapsulation
Restricting direct access to variables using private/protected members.
class Account:
def __init__(self, balance):
self.__balance = balance # private variable
def deposit(self, amount):
self.__balance += amount
def get_balance(self):
return self.__balance
a = Account(1000)
a.deposit(500)
print(a.get_balance())
19.9 Polymorphism
Different classes can define the same method name with different implementations.
class Bird:
def sound(self):
print("Bird chirps")
class Dog:
def sound(self):
print("Dog barks")
for animal in (Bird(), Dog()):
animal.sound()
19.10 Operator Overloading
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
p1 = Point(2, 3)
p2 = Point(4, 5)
p3 = p1 + p2
print(p3.x, p3.y)
19.11 Abstract Classes
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Square(Shape):
def __init__(self, side):
self.side = side
def area(self):
return self.side * self.side
s = Square(4)
print("Area:", s.area())
19.12 Summary
- Classes are blueprints, objects are instances.
__init__initializes objects.- Supports encapsulation, inheritance, polymorphism.
- Methods: instance, class, static.
- Operator overloading and abstract classes enhance OOP design.
20. Advanced Topics in Python (Full Details)
After mastering the basics of Python, it’s important to dive into advanced concepts that make your code more efficient, scalable, and professional. This section covers decorators, generators, iterators, context managers, metaclasses, asynchronous programming, and more.
20.1 Iterators
An iterator is an object that can be iterated upon using
__iter__() and __next__().
numbers = [1, 2, 3]
it = iter(numbers)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
20.2 Generators
Generators are a simple way to create iterators using yield.
They are memory-efficient because they generate values on the fly.
def my_generator():
for i in range(5):
yield i
for val in my_generator():
print(val)
20.3 Generator Expressions
squares = (x*x for x in range(5))
print(next(squares))
print(next(squares))
20.4 Decorators
A decorator is a function that modifies another function without changing its code.
def decorator(func):
def wrapper():
print("Before function call")
func()
print("After function call")
return wrapper
@decorator
def greet():
print("Hello, World!")
greet()
20.5 Function Arguments in Decorators
def log_args(func):
def wrapper(*args, **kwargs):
print("Arguments:", args, kwargs)
return func(*args, **kwargs)
return wrapper
@log_args
def add(a, b):
return a + b
print(add(3, 4))
20.6 Context Managers
Context managers handle resource allocation and cleanup (e.g., opening/closing files).
with open("example.txt", "w") as f:
f.write("Hello")
You can also create your own:
class MyContext:
def __enter__(self):
print("Entering context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting context")
with MyContext():
print("Inside context")
20.7 Lambda Functions
Anonymous single-line functions.
square = lambda x: x * x
print(square(5))
20.8 List, Dict, and Set Comprehensions
# List comprehension
squares = [x*x for x in range(5)]
# Dict comprehension
squares_dict = {x: x*x for x in range(5)}
# Set comprehension
squares_set = {x*x for x in range(5)}
20.9 Metaclasses
Metaclasses define the behavior of classes themselves (advanced OOP feature).
class Meta(type):
def __new__(cls, name, bases, dct):
print("Creating class:", name)
return super().__new__(cls, name, bases, dct)
class MyClass(metaclass=Meta):
pass
20.10 Asynchronous Programming (async/await)
asyncio is used for concurrent programming with coroutines.
import asyncio
async def task1():
print("Task 1 started")
await asyncio.sleep(1)
print("Task 1 finished")
async def task2():
print("Task 2 started")
await asyncio.sleep(2)
print("Task 2 finished")
async def main():
await asyncio.gather(task1(), task2())
asyncio.run(main())
20.11 Multithreading & Multiprocessing
import threading
def worker():
print("Worker thread")
t = threading.Thread(target=worker)
t.start()
t.join()
from multiprocessing import Process
def worker():
print("Worker process")
p = Process(target=worker)
p.start()
p.join()
20.12 Regular Expressions (Regex)
import re
pattern = r"\d+" # match digits
text = "My number is 12345"
result = re.findall(pattern, text)
print(result)
20.13 Logging
Instead of using print for debugging, use logging.
import logging
logging.basicConfig(level=logging.INFO)
logging.info("This is an info message")
20.14 Unit Testing
import unittest
def add(a, b):
return a + b
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
if __name__ == "__main__":
unittest.main()
20.15 Virtual Environments
# Create virtual environment
python -m venv myenv
# Activate (Windows)
myenv\Scripts\activate
# Activate (Linux/Mac)
source myenv/bin/activate
20.16 Packaging and Distribution
# setup.py file
from setuptools import setup, find_packages
setup(
name="mypackage",
version="0.1",
packages=find_packages()
)
20.17 Summary
- Advanced Python covers iterators, generators, and comprehensions.
- Decorators and context managers make code cleaner and reusable.
- Asynchronous programming boosts performance.
- Logging and testing improve maintainability.
- Virtual environments and packaging help in real-world project deployment.
0 Comments
Please comment your website link too. No restrictions