1. Introduction
Python is one of the most popular programming languages in the world — and for good reason.
It was created by Guido van Rossum in 1991 and has since become the go-to language for:
Data Science & Machine Learning (pandas, NumPy, TensorFlow, PyTorch)
Web Development (Django, FastAPI, Flask)
Automation & Scripting (file handling, web scraping, bots)
Data Engineering (ETL pipelines, Airflow, Spark)
DevOps & Cloud (AWS Lambda, Docker scripts, CI/CD automation)
Cybersecurity & Penetration Testing
Finance & Algorithmic Trading
Why Python?
Reads almost like English — very beginner-friendly
Massive community and libraries
Works on Windows, Mac, Linux
Used at Google, Netflix, NASA, Instagram, and thousands of companies
One of the highest-paid skills in tech
The Philosophy of Python
Run this in your Python terminal to see what Python stands for:
import this
You'll see The Zen of Python — 19 guiding principles like:
"Readability counts." and "Simple is better than complex."
2. Setting Up Python
Installing Python
# Check if Python is already installed
python --version
# or
python3 --version
py --version # Windows shorthand
Download from: https://python.org/downloads
Always install Python 3.x — Python 2 is officially retired.
Running Python
# Interactive Mode (REPL — Read Evaluate Print Loop)
python
# Run a file
python my_script.py
python3 my_script.py
# Run inline code
python -c "print('Hello, World!')"
Your First Python Program
# hello.py
print("Hello, World!")
Output:
Hello, World!
That's it. No semicolons. No curly braces. No public static void main. Just pure simplicity.
3. Basic Concepts
Comments
# This is a single-line comment
"""
This is a
multi-line comment (docstring)
"""
# Best practice: comments should explain WHY, not WHAT
x = x + 1 # Bad comment: increment x by 1
x = x + 1 # Good comment: compensate for zero-based index offset
Python is Case-Sensitive
name = "Alice"
Name = "Bob"
NAME = "Charlie"
print(name) # Alice
print(Name) # Bob
print(NAME) # Charlie
# These are THREE different variables!
Indentation — Python's Core Rule
Python uses indentation (spaces or tabs) to define code blocks. This is NOT optional.
# CORRECT
if True:
print("This is indented correctly")
# WRONG — IndentationError
if True:
print("This will crash")
Use 4 spaces per level. Never mix tabs and spaces.
4. Variables and Data Types
What is a Variable?
A variable is a named container that stores data in memory. Think of it like a labeled box.
name = "Himanshu" # str → string
age = 25 # int → integer
height = 5.9 # float → decimal
is_active = True # bool → boolean
nothing = None # NoneType → represents absence of value
Dynamic Typing
Python figures out the type automatically — you don't declare it.
x = 100 # x is int
x = "Hello" # Now x is str — Python is fine with this
x = [1, 2, 3] # Now x is list
print(type(x)) # <class 'list'>
Checking Data Types
print(type(42)) # <class 'int'>
print(type(3.14)) # <class 'float'>
print(type("Hello")) # <class 'str'>
print(type(True)) # <class 'bool'>
print(type(None)) # <class 'NoneType'>
print(type([1, 2, 3])) # <class 'list'>
print(type((1, 2))) # <class 'tuple'>
print(type({1, 2})) # <class 'set'>
print(type({"a": 1})) # <class 'dict'>
isinstance() — The Better Type Check
x = 42
print(isinstance(x, int)) # True
print(isinstance(x, (int, float))) # True — checks multiple types at once
print(isinstance(x, str)) # False
All Python Data Types
Type | Example | Mutable? |
|---|---|---|
|
| No |
|
| No |
|
| No |
|
| No |
|
| No |
|
| Yes |
|
| No |
|
| Yes |
|
| No |
|
| Yes |
|
| No |
|
| Yes |
|
| No |
Multiple Assignment
# Assign same value to multiple variables
a = b = c = 0
print(a, b, c) # 0 0 0
# Assign different values in one line (tuple unpacking)
x, y, z = 1, 2, 3
print(x, y, z) # 1 2 3
# Swap two variables (Python magic!)
a, b = 10, 20
a, b = b, a # Swap without temp variable
print(a, b) # 20 10
Constants
Python doesn't have a built-in const keyword. Convention: use ALL_CAPS.
MAX_CONNECTIONS = 5000
PI = 3.14159
DATABASE_URL = "postgresql://localhost/mydb"
API_KEY = "abc123" # Never hardcode in production!
Type Conversion (Casting)
# int conversions
print(int("42")) # 42
print(int(3.9)) # 3 (truncates, does NOT round)
print(int(True)) # 1
print(int(False)) # 0
# float conversions
print(float("3.14")) # 3.14
print(float(42)) # 42.0
# str conversions
print(str(100)) # "100"
print(str(3.14)) # "3.14"
print(str(True)) # "True"
# bool conversions — IMPORTANT to understand
print(bool(0)) # False
print(bool(1)) # True
print(bool(-1)) # True (any non-zero is True)
print(bool("")) # False (empty string is False)
print(bool("hello")) # True
print(bool([])) # False (empty list is False)
print(bool([1, 2])) # True
print(bool(None)) # False
# list, tuple, set conversions
print(list("abc")) # ['a', 'b', 'c']
print(tuple([1, 2, 3])) # (1, 2, 3)
print(set([1, 2, 2, 3])) # {1, 2, 3} — removes duplicates!
5. All Operators in Python
Arithmetic Operators
a, b = 10, 3
print(a + b) # 13 — Addition
print(a - b) # 7 — Subtraction
print(a * b) # 30 — Multiplication
print(a / b) # 3.333... — True Division (always float)
print(a // b) # 3 — Floor Division (integer result)
print(a % b) # 1 — Modulus (remainder)
print(a ** b) # 1000 — Exponentiation (10^3)
# Practical examples
print(7 / 2) # 3.5 — Normal division
print(7 // 2) # 3 — Floor division (drops decimal)
print(7 % 2) # 1 — Remainder (is 7 odd? yes, remainder = 1)
print(2 ** 8) # 256 — 2 to the power of 8
Assignment Operators
x = 10 # Basic assignment
x += 5 # x = x + 5 → 15
x -= 3 # x = x - 3 → 12
x *= 2 # x = x * 2 → 24
x /= 4 # x = x / 4 → 6.0
x //= 2 # x = x // 2 → 3.0
x %= 2 # x = x % 2 → 1.0
x **= 3 # x = x ** 3 → 1.0
Comparison Operators
a, b = 10, 20
print(a == b) # False — Equal to
print(a != b) # True — Not equal to
print(a > b) # False — Greater than
print(a < b) # True — Less than
print(a >= b) # False — Greater than or equal
print(a <= b) # True — Less than or equal
# Chained comparison (Python-specific!)
x = 5
print(1 < x < 10) # True — between 1 and 10
print(1 < x < 4) # False
Logical Operators
# and — both must be True
print(True and True) # True
print(True and False) # False
# or — at least one must be True
print(True or False) # True
print(False or False) # False
# not — flips the boolean
print(not True) # False
print(not False) # True
# Operator Precedence: not → and → or
result = True or False and False
# Evaluated as: True or (False and False) → True or False → True
print(result) # True
result = not True or False
# Evaluated as: (not True) or False → False or False → False
print(result) # False
Membership Operators
my_list = [1, 2, 3, 4, 5]
my_str = "Hello, Python!"
# in — checks if value exists
print(3 in my_list) # True
print(6 in my_list) # False
print("Python" in my_str) # True
print("Java" in my_str) # False
# not in — checks if value does NOT exist
print(6 not in my_list) # True
print(3 not in my_list) # False
Identity Operators
# is — checks if two variables point to the SAME object in memory
# == — checks if two variables have the SAME VALUE
a = [1, 2, 3]
b = a # b points to the SAME list
c = [1, 2, 3] # c is a NEW list with same content
print(a == b) # True (same value)
print(a is b) # True (same object in memory)
print(a == c) # True (same value)
print(a is c) # False (different object — different memory address)
# is None — ALWAYS use 'is' to check None, not '=='
x = None
print(x is None) # True ✅ Correct
print(x == None) # True, but not Pythonic ❌
# Small integers are cached in Python (CPython optimization)
x = 5
y = 5
print(x is y) # True — Python caches small integers (-5 to 256)
x = 1000
y = 1000
print(x is y) # False — Large integers are NOT cached
Bitwise Operators
a = 0b1010 # 10 in binary
b = 0b1100 # 12 in binary
print(a & b) # 8 — AND: 1010 & 1100 = 1000
print(a | b) # 14 — OR: 1010 | 1100 = 1110
print(a ^ b) # 6 — XOR: 1010 ^ 1100 = 0110
print(~a) # -11 — NOT: flips all bits
print(a << 1) # 20 — Left shift (multiply by 2)
print(a >> 1) # 5 — Right shift (divide by 2)
6. Strings — Deep Dive
Strings are one of the most-used data types in Python. Let's master them.
Creating Strings
# Single quotes
s1 = 'Hello'
# Double quotes
s2 = "World"
# Triple quotes (multi-line strings)
s3 = """This is
a multi-line
string."""
s4 = '''Another
multi-line string'''
# Raw strings (no escape sequences)
s5 = r"C:\Users\Himanshu\Documents" # Backslash treated as literal
print(s5) # C:\Users\Himanshu\Documents
# F-strings (Python 3.6+) — most modern and recommended
name = "Himanshu"
age = 25
s6 = f"My name is {name} and I am {age} years old."
print(s6) # My name is Himanshu and I am 25 years old.
Escape Characters
print("Hello\nWorld") # Newline
print("Hello\tWorld") # Tab
print("Hello\\World") # Backslash
print("He said \"Hi\"") # Double quote
print('It\'s Python') # Single quote
print("\u2764") # Unicode heart ❤
print("\n" * 2) # Two newlines
Escape | Meaning |
|---|---|
| Newline |
| Tab |
| Backslash |
| Double quote |
| Single quote |
| Carriage return |
| Null character |
| Unicode character |
| Backspace |
String Indexing and Slicing
a = '0123456789'
# 0123456789 ← Positive index
# -10-9-8-7-6-5-4-3-2-1 ← Negative index
# Indexing (single character)
print(a[0]) # '0' — First character
print(a[9]) # '9' — Last character
print(a[-1]) # '9' — Last character (negative indexing)
print(a[-2]) # '8' — Second to last
# Slicing: a[start:stop:step]
# start: inclusive, stop: exclusive, step: optional
print(a[1:5]) # '1234' — from index 1 to 4
print(a[5:]) # '56789' — from index 5 to end
print(a[:4]) # '0123' — from beginning to index 3
print(a[::2]) # '02468' — every 2nd character
print(a[1::2]) # '13579' — from 1, every 2nd character
print(a[::-1]) # '9876543210' — reversed string!
print(a[0]) # '0'
print(a[1:5]) # '1234'
print(a[5:]) # '56789'
print(a[1::2]) # '13579'
print(a[::2]) # '02468'
print(a[::-1]) # '9876543210'
print(a[-1]) # '9' reversed indexing: len(a)-1 = 9
print(a[-2]) # '8' reversed indexing: len(a)-2 = 8
String Methods — Complete Reference
text = " Hello, Python World! "
# Case Methods
print(text.upper()) # " HELLO, PYTHON WORLD! "
print(text.lower()) # " hello, python world! "
print(text.title()) # " Hello, Python World! "
print(text.capitalize()) # " hello, python world! " (only first char of string)
print(text.swapcase()) # " hELLO, pYTHON wORLD! "
# Whitespace Removal
print(text.strip()) # "Hello, Python World!" — both sides
print(text.lstrip()) # "Hello, Python World! " — left only
print(text.rstrip()) # " Hello, Python World!" — right only
# Finding and Replacing
text2 = "Python is fun and Python is popular"
print(text2.find("Python")) # 0 — first occurrence index
print(text2.rfind("Python")) # 18 — last occurrence index
print(text2.find("Java")) # -1 — not found
print(text2.index("Python")) # 0 — like find, but raises ValueError if not found
print(text2.count("Python")) # 2 — count occurrences
print(text2.replace("Python", "Java")) # "Java is fun and Java is popular"
print(text2.replace("Python", "Java", 1)) # Replace only first occurrence
# Splitting and Joining
csv_line = "apple,banana,cherry,date"
fruits = csv_line.split(",") # ['apple', 'banana', 'cherry', 'date']
print(fruits)
joined = " | ".join(fruits) # 'apple | banana | cherry | date'
print(joined)
print("hello world".split()) # ['hello', 'world'] — splits on whitespace
print("a-b-c".split("-", 1)) # ['a', 'b-c'] — split only once
# Checking String Content
print("Python123".isalpha()) # False — not all alphabetic
print("Python".isalpha()) # True
print("123".isdigit()) # True
print("12.3".isdigit()) # False — decimal point is not digit
print("Python123".isalnum()) # True — alphanumeric
print(" ".isspace()) # True
print("hello".startswith("hel")) # True
print("hello".endswith("lo")) # True
print("HELLO".isupper()) # True
print("hello".islower()) # True
print("Hello World".istitle()) # True
# Padding and Alignment
print("hello".center(20)) # " hello "
print("hello".center(20, "-")) # "-------hello--------"
print("hello".ljust(20, ".")) # "hello..............."
print("hello".rjust(20, ".")) # "...............hello"
print("42".zfill(5)) # "00042" — zero-fill (great for numbers)
# Prefix/Suffix Removal (Python 3.9+)
print("https://example.com".removeprefix("https://")) # "example.com"
print("https://example.com".removesuffix(".com")) # "https://example"
# Encoding
print("hello".encode("utf-8")) # b'hello' — bytes object
print(b"hello".decode("utf-8")) # 'hello' — back to string
# String Formatting (3 styles)
name, age = "Himanshu", 25
# 1. f-strings (Modern — Python 3.6+, RECOMMENDED)
print(f"Hello {name}, you are {age} years old.")
print(f"Pi = {3.14159:.2f}") # Pi = 3.14 — format to 2 decimal places
print(f"{'hello':>10}") # " hello" — right align in 10 chars
print(f"{1000000:,}") # "1,000,000" — thousand separator
# 2. .format() method
print("Hello {}, you are {} years old.".format(name, age))
print("Hello {0}, you are {1}, {0}!".format(name, age)) # reuse position
print("Hello {name}, you are {age}.".format(name=name, age=age))
# 3. % operator (Old style — avoid in new code)
print("Hello %s, you are %d years old." % (name, age))
print("Pi = %.2f" % 3.14159)
String Immutability
s = "Hello"
# s[0] = "h" # ❌ TypeError: 'str' object does not support item assignment
# To "modify" a string, create a new one
s = "h" + s[1:] # "hello"
print(s) # "hello"
Multi-line Strings and String Concatenation
# Concatenation with +
greeting = "Hello" + ", " + "World!"
print(greeting) # Hello, World!
# Repetition with *
line = "-" * 50
print(line) # --------------------------------------------------
# Multi-line f-string
name, salary, dept = "Alice", 75000, "Engineering"
report = f"""
Employee Report
---------------
Name: {name}
Department: {dept}
Salary: ${salary:,}
"""
print(report)
7. User Input and Output
The print() Function — All Options
# Basic print
print("Hello, World!") # Hello, World!
# Multiple arguments (default sep is space)
print("Hello", "World", 5, 6) # Hello World 5 6
# Custom separator
print("2025", "01", "15", sep="-") # 2025-01-15
print("Hello", "World", sep=", ") # Hello, World
# Custom end (default is newline \n)
print("Hello", end=" ")
print("World") # Hello World (same line)
print("Hello", end="")
print("World") # HelloWorld
# Multiple outputs in one line
print("A", "B", "C", sep=" | ", end=" !!!\n") # A | B | C !!!
# Print to file (useful for logging)
import sys
print("Error message", file=sys.stderr)
# flush — force immediate output (useful in loops)
import time
for i in range(5):
print(f"\rLoading: {i+1}/5", end="", flush=True)
time.sleep(0.5)
print() # Final newline
The input() Function
# Basic input — always returns a string
name = input("Enter your name: ")
print(f"Hello, {name}!")
# Integer input
age = int(input("Enter your age: "))
print(f"You are {age} years old.")
# Float input
height = float(input("Enter your height in meters: "))
# Multiple inputs on one line
x, y = input("Enter two numbers separated by space: ").split()
x, y = int(x), int(y)
print(f"Sum = {x + y}")
# Safer input with error handling
try:
age = int(input("Enter your age: "))
except ValueError:
print("Please enter a valid integer!")
age = 0
8. Control Flow
if / elif / else
# Basic if-else
age = 20
if age >= 18:
print("You are an adult.")
else:
print("You are a minor.")
# if-elif-else chain
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
else:
grade = "F"
print(f"Your grade is: {grade}") # B
# Nested if
x = 15
if x > 0:
if x % 2 == 0:
print("Positive even")
else:
print("Positive odd") # This prints
else:
print("Negative or zero")
Ternary / Conditional Expression
# Traditional
if age >= 18:
status = "Adult"
else:
status = "Minor"
# Pythonic one-liner
status = "Adult" if age >= 18 else "Minor"
print(status)
# Nested ternary (use sparingly — can hurt readability)
label = "High" if score >= 80 else "Medium" if score >= 60 else "Low"
Match Statement (Python 3.10+)
The match statement is Python's pattern matching — like a supercharged switch.
# Basic match
status = 404
match status:
case 200:
print("OK")
case 301 | 302:
print("Redirect")
case 404:
print("Not Found")
case 500:
print("Internal Server Error")
case _: # Default case
print("Unknown Status Code")
# Match with data structures
point = (1, 0)
match point:
case (0, 0):
print("Origin")
case (x, 0):
print(f"On X-axis at {x}")
case (0, y):
print(f"On Y-axis at {y}")
case (x, y):
print(f"Point at ({x}, {y})")
# Match with conditions (guards)
number = 42
match number:
case n if n < 0:
print("Negative")
case n if n == 0:
print("Zero")
case n if n % 2 == 0:
print(f"{n} is positive and even") # This prints
case _:
print("Positive and odd")
9. Loops
for Loop
# Loop over a list
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# Loop over a string
for char in "Python":
print(char) # P y t h o n (each on new line)
# Loop with range()
for i in range(5): # 0 1 2 3 4
print(i)
for i in range(1, 6): # 1 2 3 4 5
print(i)
for i in range(0, 10, 2): # 0 2 4 6 8 — step of 2
print(i)
for i in range(10, 0, -1): # 10 9 8 ... 1 — countdown
print(i)
# enumerate() — get index AND value
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry
for index, fruit in enumerate(fruits, start=1): # Start from 1
print(f"{index}. {fruit}")
# zip() — loop over two lists simultaneously
names = ["Alice", "Bob", "Charlie"]
scores = [95, 87, 78]
for name, score in zip(names, scores):
print(f"{name}: {score}")
Loop Control: break, continue, pass
# break — exit the loop entirely
for i in range(1, 10):
if i == 5:
break # Stop at 5
print(i) # 1 2 3 4
# continue — skip current iteration, continue loop
for i in range(1, 10):
if i == 5:
continue # Skip 5
print(i) # 1 2 3 4 6 7 8 9
# pass — do nothing (placeholder for future code)
for i in range(5):
if i == 3:
pass # Will handle this case later
print(i) # 1 2 3 4 5 (prints all, pass does nothing)
# for-else — the else runs if loop completes WITHOUT break
for i in range(1, 6):
if i == 10: # 10 never found
break
else:
print("10 not found in range!") # This prints
# Practical break-else: search for item
names = ["Alice", "Bob", "Charlie"]
search = "Bob"
for name in names:
if name == search:
print(f"Found: {name}")
break
else:
print(f"{search} not found")
while Loop
# Basic while
count = 0
while count < 5:
print(count) # 0 1 2 3 4
count += 1
# While with break (infinite loop pattern)
while True:
user_input = input("Type 'quit' to exit: ")
if user_input.lower() == "quit":
print("Goodbye!")
break
print(f"You typed: {user_input}")
# while-else
n = 10
while n > 0:
n -= 1
else:
print("Loop finished normally") # Runs because no break occurred
Nested Loops
# Multiplication table
for i in range(1, 4):
for j in range(1, 4):
print(f"{i} x {j} = {i*j}", end="\t")
print() # Newline after each row
# Output:
# 1 x 1 = 1 1 x 2 = 2 1 x 3 = 3
# 2 x 1 = 2 2 x 2 = 4 2 x 3 = 6
# 3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
10. Functions
Defining and Calling Functions
# Basic function
def greet():
print("Hello, World!")
greet() # Call the function
# Function with parameters
def greet_user(name):
print(f"Hello, {name}!")
greet_user("Alice") # Hello, Alice!
greet_user("Bob") # Hello, Bob!
# Function with return value
def add(a, b):
return a + b
result = add(3, 4)
print(result) # 7
Function Parameters — All Types
# 1. Positional arguments
def full_name(first, last):
return f"{first} {last}"
print(full_name("John", "Doe")) # John Doe
# 2. Keyword arguments
print(full_name(last="Doe", first="John")) # John Doe — order doesn't matter
# 3. Default parameter values
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Alice", "Hi")) # Hi, Alice!
print(greet("Alice", greeting="Hey")) # Hey, Alice!
# 4. *args — variable positional arguments
def sum_all(*numbers):
return sum(numbers)
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
# 5. **kwargs — variable keyword arguments
def display_info(**info):
for key, value in info.items():
print(f" {key}: {value}")
display_info(name="Alice", age=30, city="NYC")
# 6. Combining all parameter types
def example(a, b, *args, key="default", **kwargs):
print(f"a={a}, b={b}")
print(f"args={args}")
print(f"key={key}")
print(f"kwargs={kwargs}")
example(1, 2, 3, 4, 5, key="custom", x=10, y=20)
Return Values
# Return multiple values (returns a tuple)
def min_max(numbers):
return min(numbers), max(numbers)
minimum, maximum = min_max([3, 1, 4, 1, 5, 9, 2])
print(f"Min: {minimum}, Max: {maximum}") # Min: 1, Max: 9
# Early return
def divide(a, b):
if b == 0:
return None # Early exit
return a / b
# None is returned implicitly if no return statement
def just_print(x):
print(x)
result = just_print(5) # Prints 5
print(result) # None
Lambda Functions
# Lambda: anonymous one-line function
# Syntax: lambda arguments: expression
square = lambda x: x ** 2
print(square(5)) # 25
add = lambda x, y: x + y
print(add(3, 4)) # 7
# Common use with sorted(), map(), filter()
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
# Sort list
sorted_nums = sorted(numbers)
print(sorted_nums) # [1, 1, 2, 3, 4, 5, 6, 9]
# Sort list of tuples by second element
pairs = [(1, 'b'), (2, 'a'), (3, 'c')]
pairs.sort(key=lambda pair: pair[1])
print(pairs) # [(2, 'a'), (1, 'b'), (3, 'c')]
# map() — apply function to each element
squares = list(map(lambda x: x**2, [1, 2, 3, 4, 5]))
print(squares) # [1, 4, 9, 16, 25]
# filter() — keep elements where function returns True
evens = list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6]))
print(evens) # [2, 4, 6]
Scope: Local vs Global
# Global scope
x = "global"
def my_func():
# Local scope — creates a NEW local variable
x = "local"
print(x) # local
my_func()
print(x) # global — unchanged
# global keyword — modify global variable from inside function
counter = 0
def increment():
global counter
counter += 1
increment()
increment()
print(counter) # 2
# nonlocal keyword — modify enclosing function's variable
def outer():
count = 0
def inner():
nonlocal count
count += 1
inner()
inner()
print(count) # 2
outer()
11. Intermediate Usage
List Comprehensions
# Traditional for loop
squares = []
for i in range(1, 6):
squares.append(i**2)
print(squares) # [1, 4, 9, 16, 25]
# List comprehension (Pythonic way)
squares = [i**2 for i in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
# With condition (filter)
evens = [i for i in range(1, 11) if i % 2 == 0]
print(evens) # [2, 4, 6, 8, 10]
# With transformation + condition
result = [i**2 for i in range(1, 11) if i % 2 == 0]
print(result) # [4, 16, 36, 64, 100]
# Nested list comprehension — flatten 2D list
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
print(flat) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Dict comprehension
squared_dict = {x: x**2 for x in range(1, 6)}
print(squared_dict) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Set comprehension
unique_squares = {x**2 for x in [-2, -1, 0, 1, 2]}
print(unique_squares) # {0, 1, 4}
# Generator expression (memory efficient)
total = sum(x**2 for x in range(1000000)) # No list created in memory
Working with Lists
# Creating
lst = [1, 2, 3, 4, 5]
empty = []
mixed = [1, "hello", 3.14, True, None]
# Accessing
print(lst[0]) # 1
print(lst[-1]) # 5
print(lst[1:3]) # [2, 3]
# Modifying
lst[0] = 10
lst.append(6) # Add to end
lst.insert(2, 99) # Insert at index 2
lst.extend([7, 8]) # Add multiple items
# Removing
lst.remove(99) # Remove first occurrence of 99
popped = lst.pop() # Remove & return last item
popped_at = lst.pop(0) # Remove & return at index
# Searching
print(3 in lst) # True/False
print(lst.index(3)) # Index of first 3
print(lst.count(3)) # Count occurrences
# Sorting
lst.sort() # In-place sort
lst.sort(reverse=True) # Descending
sorted_lst = sorted(lst) # Returns new sorted list
# Reversing
lst.reverse() # In-place
reversed_lst = lst[::-1] # New reversed list
# Copying
copy1 = lst.copy() # Shallow copy
copy2 = lst[:] # Shallow copy
import copy
deep_copy = copy.deepcopy(lst) # Deep copy (for nested lists)
# Length
print(len(lst))
# List operations
a = [1, 2, 3]
b = [4, 5, 6]
print(a + b) # [1, 2, 3, 4, 5, 6] — concatenation
print(a * 3) # [1, 2, 3, 1, 2, 3, 1, 2, 3] — repetition
Working with Tuples
# Tuples are immutable lists — great for data that shouldn't change
coordinates = (10.5, 20.3)
rgb = (255, 128, 0)
point = (3, 4)
# Unpacking
x, y = coordinates
r, g, b = rgb
print(x, y) # 10.5 20.3
# Extended unpacking
first, *rest = [1, 2, 3, 4, 5]
print(first) # 1
print(rest) # [2, 3, 4, 5]
*start, last = [1, 2, 3, 4, 5]
print(start) # [1, 2, 3, 4]
print(last) # 5
# Named tuple — like a mini class
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y) # 3 4
print(p) # Point(x=3, y=4)
Working with Dictionaries
# Creating
person = {"name": "Alice", "age": 30, "city": "NYC"}
empty_dict = {}
dict_from_lists = dict(zip(["a", "b", "c"], [1, 2, 3]))
# Accessing
print(person["name"]) # Alice
print(person.get("age")) # 30
print(person.get("email", "N/A")) # N/A — default if key missing
# Modifying
person["email"] = "alice@example.com" # Add new key
person["age"] = 31 # Update existing key
person.update({"phone": "123-456", "city": "LA"}) # Update multiple
# Removing
del person["phone"]
removed = person.pop("email") # Remove & return value
person.popitem() # Remove last inserted pair (Python 3.7+)
# Iterating
for key in person:
print(key)
for key, value in person.items():
print(f"{key}: {value}")
for value in person.values():
print(value)
# Checking keys
print("name" in person) # True
print("email" in person) # False
# Merging dicts (Python 3.9+)
d1 = {"a": 1, "b": 2}
d2 = {"c": 3, "d": 4}
merged = d1 | d2 # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
d1 |= d2 # Update d1 with d2
# Nested dict
company = {
"name": "TechCorp",
"employees": {
"ceo": "Alice",
"cto": "Bob"
},
"locations": ["NYC", "LA", "Chicago"]
}
print(company["employees"]["ceo"]) # Alice
print(company["locations"][0]) # NYC
Working with Sets
# Creating
fruits = {"apple", "banana", "cherry"}
numbers = {1, 2, 3, 4, 5}
empty_set = set() # NOT {} — that creates empty dict!
# Adding and removing
fruits.add("orange")
fruits.discard("banana") # No error if not present
fruits.remove("cherry") # Raises KeyError if not present
# Set operations
a = {1, 2, 3, 4, 5}
b = {3, 4, 5, 6, 7}
print(a | b) # {1,2,3,4,5,6,7} — Union
print(a & b) # {3,4,5} — Intersection
print(a - b) # {1,2} — Difference
print(a ^ b) # {1,2,6,7} — Symmetric difference
# Membership check is O(1) for sets — much faster than lists!
big_set = set(range(1000000))
print(999999 in big_set) # True — very fast
# Practical: Remove duplicates
names = ["Alice", "Bob", "Alice", "Charlie", "Bob"]
unique_names = list(set(names))
print(unique_names) # ['Alice', 'Bob', 'Charlie'] (order not guaranteed)
12. Advanced Concepts
Generators and Iterators
# Generator function — uses yield instead of return
def count_up(limit):
n = 0
while n < limit:
yield n # Pauses here, resumes on next()
n += 1
gen = count_up(5)
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
for num in count_up(5):
print(num) # 0 1 2 3 4
# Generator is lazy — values computed on demand
# Great for large datasets that don't fit in memory
def read_large_file(filepath):
with open(filepath) as f:
for line in f:
yield line.strip()
# Generator expression (memory efficient)
million_squares = (x**2 for x in range(1_000_000))
print(next(million_squares)) # 0
print(next(million_squares)) # 1
Decorators
# A decorator wraps a function to add behavior
def timer(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end-start:.4f} seconds")
return result
return wrapper
@timer
def slow_function():
import time
time.sleep(1)
return "Done"
slow_function() # slow_function took 1.0012 seconds
# Decorator with arguments
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello() # Prints Hello! three times
Error Handling
# try-except-else-finally
def divide(a, b):
try:
result = a / b
except ZeroDivisionError:
print("Cannot divide by zero!")
return None
except TypeError as e:
print(f"Type error: {e}")
return None
else:
# Runs if no exception occurred
print("Division successful!")
return result
finally:
# ALWAYS runs — perfect for cleanup
print("Division attempted.")
divide(10, 2) # Division successful! Division attempted. → 5.0
divide(10, 0) # Cannot divide by zero! Division attempted. → None
# Catching multiple exceptions
try:
x = int(input("Enter number: "))
except (ValueError, TypeError) as e:
print(f"Error: {e}")
# Custom exceptions
class InvalidAgeError(Exception):
def __init__(self, age, message="Age must be positive"):
self.age = age
self.message = message
super().__init__(self.message)
def set_age(age):
if age < 0:
raise InvalidAgeError(age, f"{age} is not a valid age!")
return age
try:
set_age(-5)
except InvalidAgeError as e:
print(f"Caught: {e}") # Caught: -5 is not a valid age!
File Handling
# Writing a file
with open("example.txt", "w") as f:
f.write("Hello, World!\n")
f.write("Second line\n")
# Reading a file
with open("example.txt", "r") as f:
content = f.read() # Read entire file
print(content)
# Read line by line (memory efficient)
with open("example.txt", "r") as f:
for line in f:
print(line.strip())
# Read all lines into a list
with open("example.txt", "r") as f:
lines = f.readlines()
# Append to file
with open("example.txt", "a") as f:
f.write("Third line\n")
# File modes
# 'r' — Read (default)
# 'w' — Write (overwrites)
# 'a' — Append
# 'x' — Create new (fails if exists)
# 'b' — Binary mode (rb, wb)
# '+' — Read and write (r+, w+)
Comprehensions at Scale
# Dict comprehension — word count
sentence = "the cat sat on the mat the cat"
word_count = {word: sentence.split().count(word) for word in set(sentence.split())}
print(word_count) # {'the': 3, 'cat': 2, 'sat': 1, 'on': 1, 'mat': 1}
# Conditional dict comprehension
scores = {"Alice": 92, "Bob": 67, "Charlie": 88, "Dave": 55}
passed = {name: score for name, score in scores.items() if score >= 70}
print(passed) # {'Alice': 92, 'Charlie': 88}
13. Real-World Use Cases
Use Case 1: CSV Data Processing
import csv
# Read CSV
def read_sales_data(filename):
sales = []
with open(filename, "r") as f:
reader = csv.DictReader(f)
for row in reader:
sales.append({
"product": row["product"],
"quantity": int(row["quantity"]),
"price": float(row["price"])
})
return sales
# Process data
def analyze_sales(sales):
total_revenue = sum(item["quantity"] * item["price"] for item in sales)
best_seller = max(sales, key=lambda x: x["quantity"])
return {"total_revenue": total_revenue, "best_seller": best_seller}
Use Case 2: Config File Parser
import json
import os
def load_config(path="config.json"):
"""Load application configuration from JSON file."""
try:
with open(path, "r") as f:
config = json.load(f)
# Override with environment variables
config["db_host"] = os.getenv("DB_HOST", config.get("db_host", "localhost"))
config["debug"] = os.getenv("DEBUG", str(config.get("debug", False))).lower() == "true"
return config
except FileNotFoundError:
print(f"Config file {path} not found. Using defaults.")
return {"db_host": "localhost", "debug": False}
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in config file: {e}")
config = load_config()
print(f"Connecting to: {config['db_host']}")
Use Case 3: Simple Data Pipeline
# Extract → Transform → Load pattern
def extract(filepath):
"""Read raw data from file."""
with open(filepath) as f:
return [line.strip().split(",") for line in f if line.strip()]
def transform(raw_data):
"""Clean and structure data."""
header = raw_data[0]
records = []
for row in raw_data[1:]:
record = dict(zip(header, row))
record["salary"] = float(record.get("salary", 0))
record["name"] = record.get("name", "").title()
records.append(record)
return records
def load(data, output_file):
"""Save processed data."""
import json
with open(output_file, "w") as f:
json.dump(data, f, indent=2)
print(f"Saved {len(data)} records to {output_file}")
# Run pipeline
raw = extract("employees.csv")
clean = transform(raw)
load(clean, "employees_clean.json")
Use Case 4: Web Scraping (Basic)
# Install: pip install requests beautifulsoup4
import requests
from bs4 import BeautifulSoup
def scrape_headlines(url):
headers = {"User-Agent": "Mozilla/5.0"}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # Raise exception for bad status
soup = BeautifulSoup(response.text, "html.parser")
headlines = []
for h2 in soup.find_all("h2", limit=10):
text = h2.get_text(strip=True)
if text:
headlines.append(text)
return headlines
# headlines = scrape_headlines("https://news.ycombinator.com")
14. Practical Examples
Example 1: Number Guessing Game
# number_guessing.py — Complete interactive game
import random
def play_guessing_game():
"""
Simple number guessing game demonstrating:
- while loops
- if-elif-else
- user input
- f-strings
- functions
"""
secret = random.randint(1, 100) # Random number between 1 and 100
attempts = 0
max_attempts = 7
print("🎮 Welcome to the Number Guessing Game!")
print(f"I've picked a number between 1 and 100.")
print(f"You have {max_attempts} attempts. Go!\n")
while attempts < max_attempts:
try:
guess = int(input(f"Attempt {attempts + 1}/{max_attempts} — Your guess: "))
except ValueError:
print("⚠️ Please enter a valid integer.")
continue
attempts += 1
if guess == secret:
print(f"\n🎉 Correct! The number was {secret}.")
print(f"You got it in {attempts} attempt(s)!")
return True
elif guess < secret:
remaining = max_attempts - attempts
print(f"📈 Too low! {remaining} attempt(s) left.")
else:
remaining = max_attempts - attempts
print(f"📉 Too high! {remaining} attempt(s) left.")
print(f"\n😢 Game over! The secret number was {secret}.")
return False
# Run the game
play_guessing_game()
Line-by-line explanation:
random.randint(1, 100)— generates a random integer between 1 and 100 (both inclusive)while attempts < max_attempts— runs until player runs out of triestry/except ValueError— gracefully handles non-integer inputscontinue— skips to next iteration if input was invalid (doesn't count as attempt)attempts += 1— increment only after valid inputreturn True/False— function signals win or loss to caller
Example 2: Student Grade Calculator
# grade_calculator.py
def calculate_grade(scores):
"""
Takes a list of scores and returns detailed grade analysis.
Demonstrates: functions, lists, dict, f-strings, loops.
"""
if not scores:
return {"error": "No scores provided"}
total = sum(scores) # Sum all scores
average = total / len(scores) # Calculate average
highest = max(scores) # Maximum score
lowest = min(scores) # Minimum score
# Determine letter grade
if average >= 90:
letter = "A"
remark = "Excellent! 🏆"
elif average >= 80:
letter = "B"
remark = "Good work! 👍"
elif average >= 70:
letter = "C"
remark = "Average. 📚"
elif average >= 60:
letter = "D"
remark = "Below average. 📖"
else:
letter = "F"
remark = "Needs improvement. ⚠️"
return {
"scores": scores,
"total": total,
"average": round(average, 2),
"highest": highest,
"lowest": lowest,
"grade": letter,
"remark": remark,
"passed": average >= 60
}
def display_report(student_name, scores):
"""Display formatted student report."""
result = calculate_grade(scores)
print(f"\n{'='*40}")
print(f" STUDENT REPORT: {student_name.upper()}")
print(f"{'='*40}")
print(f" Subjects scored: {result['scores']}")
print(f" Total: {result['total']}")
print(f" Average: {result['average']}")
print(f" Highest: {result['highest']}")
print(f" Lowest: {result['lowest']}")
print(f" Grade: {result['grade']}")
print(f" Status: {'PASS ✅' if result['passed'] else 'FAIL ❌'}")
print(f" Remark: {result['remark']}")
print(f"{'='*40}\n")
# Test it
display_report("Himanshu Sharma", [85, 92, 78, 90, 88])
display_report("Bob Jones", [45, 55, 40, 50, 60])
Example 3: Word Frequency Analyzer
# word_frequency.py
def analyze_text(text):
"""
Analyzes a block of text and returns word frequency statistics.
Demonstrates: string methods, dict, list comprehension, sorting.
"""
# Clean and split
words = text.lower().split()
# Remove punctuation from each word
import string
clean_words = [word.strip(string.punctuation) for word in words]
clean_words = [word for word in clean_words if word] # Remove empty strings
# Count frequencies using dict
frequency = {}
for word in clean_words:
frequency[word] = frequency.get(word, 0) + 1
# Sort by frequency (descending), then alphabetically
sorted_words = sorted(frequency.items(), key=lambda x: (-x[1], x[0]))
return {
"total_words": len(clean_words),
"unique_words": len(frequency),
"top_10": sorted_words[:10],
"frequency": frequency
}
text = """
Python is an amazing programming language. Python is used in data science,
web development, automation, and artificial intelligence. Python developers
love Python because Python is readable, clean, and powerful.
"""
result = analyze_text(text)
print(f"Total words: {result['total_words']}")
print(f"Unique words: {result['unique_words']}")
print("\nTop 10 most frequent words:")
for word, count in result["top_10"]:
bar = "█" * count
print(f" {word:<15} {count:>3} {bar}")
15. Edge Cases and Errors
Common Mistakes and How to Fix Them
# ❌ MISTAKE 1: Using mutable default argument
def add_item(item, lst=[]): # WRONG — list persists across calls!
lst.append(item)
return lst
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] — BUG! Expected ['b']
# ✅ FIX: Use None as default
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
print(add_item("a")) # ['a']
print(add_item("b")) # ['b'] ✅
# ❌ MISTAKE 2: Modifying list while iterating
numbers = [1, 2, 3, 4, 5, 6]
for n in numbers:
if n % 2 == 0:
numbers.remove(n) # WRONG — skips elements!
print(numbers) # [1, 3, 5] — looks right but could be wrong in other cases
# ✅ FIX: Iterate over a copy
for n in numbers[:]: # numbers[:] creates a copy
if n % 2 == 0:
numbers.remove(n)
# Even better:
numbers = [n for n in numbers if n % 2 != 0]
# ❌ MISTAKE 3: Using == to compare with None
x = None
if x == None: # Works but not Pythonic
print("x is None")
# ✅ FIX:
if x is None: # Correct Pythonic way
print("x is None")
# ❌ MISTAKE 4: Integer division confusion
print(7 / 2) # 3.5 — float
print(7 // 2) # 3 — integer
# Many beginners expect 7/2 to be 3
# ❌ MISTAKE 5: String + non-string concatenation
name = "Alice"
age = 30
# print("Hello " + name + " you are " + age) # ❌ TypeError!
# ✅ FIX:
print("Hello " + name + " you are " + str(age)) # Cast to str
print(f"Hello {name} you are {age}") # f-string is best
# ❌ MISTAKE 6: Shallow copy gotcha
original = [[1, 2], [3, 4]]
shallow = original.copy() # Only top-level copied!
shallow[0][0] = 999 # Modifies BOTH lists!
print(original) # [[999, 2], [3, 4]] — BUG!
# ✅ FIX: Deep copy for nested structures
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 999
print(original) # [[1, 2], [3, 4]] ✅ Unchanged
# ❌ MISTAKE 7: Off-by-one in range
for i in range(10): # 0 to 9 (NOT 10!)
pass
for i in range(1, 11): # 1 to 10 (include 10)
pass
# ❌ MISTAKE 8: Global variable modification without 'global'
count = 0
def bad_increment():
count += 1 # UnboundLocalError! Python sees count as local
def good_increment():
global count
count += 1 # Works correctly
Common Error Types and Solutions
Error | Cause | Fix |
|---|---|---|
| Variable used before assignment | Check spelling or define variable first |
| Wrong type operation | Cast with |
| List index out of range | Check list length, use |
| Dict key doesn't exist | Use |
| Wrong value for type | Use |
| Method doesn't exist on object | Check the type with |
| Wrong indentation | Use 4 spaces consistently |
| Dividing by zero | Check denominator before dividing |
| Infinite recursion | Add base case to recursive function |
| Iterator exhausted | Use |
16. Pro Developer Insights
Best Practices
# 1. Use f-strings for formatting (fastest and most readable)
name, age = "Alice", 30
print(f"Name: {name}, Age: {age}") # ✅ Best
print("Name: {}, Age: {}".format(name, age)) # OK
print("Name: %s, Age: %d" % (name, age)) # Old, avoid
# 2. Use enumerate() instead of range(len())
items = ["a", "b", "c"]
# ❌ Old way
for i in range(len(items)):
print(i, items[i])
# ✅ Pythonic
for i, item in enumerate(items):
print(i, item)
# 3. Use zip() to iterate over multiple lists
names = ["Alice", "Bob"]
scores = [95, 87]
# ❌ Old way
for i in range(len(names)):
print(names[i], scores[i])
# ✅ Pythonic
for name, score in zip(names, scores):
print(name, score)
# 4. Use list comprehensions for simple transformations
# ❌
result = []
for x in range(10):
result.append(x**2)
# ✅
result = [x**2 for x in range(10)]
# 5. Unpack tuples directly
point = (3, 4)
# ❌
x = point[0]
y = point[1]
# ✅
x, y = point
# 6. Use 'with' for file operations (ensures cleanup)
# ❌
f = open("file.txt")
data = f.read()
f.close() # What if an exception happens before this?
# ✅
with open("file.txt") as f:
data = f.read() # File automatically closed even on exception
# 7. Use .get() for dict access to avoid KeyError
data = {"name": "Alice"}
# ❌
# print(data["email"]) # KeyError!
# ✅
print(data.get("email", "No email")) # No email
# 8. Boolean checks — be Pythonic
my_list = [1, 2, 3]
# ❌
if len(my_list) > 0:
print("list is not empty")
# ✅
if my_list:
print("list is not empty")
# 9. Walrus operator := (Python 3.8+)
# Assign and test in one expression
import re
if match := re.search(r"\d+", "There are 42 items"):
print(f"Found number: {match.group()}") # Found number: 42
# 10. Use _ for unused variables
for _ in range(5): # I don't need the index
print("Hello")
first, _, last = (1, 2, 3) # I don't need the middle value
Naming Conventions (PEP 8)
# Variables and functions — snake_case
user_name = "Alice"
def calculate_total_price():
pass
# Classes — PascalCase
class UserAccount:
pass
# Constants — UPPER_SNAKE_CASE
MAX_RETRY_COUNT = 3
DATABASE_URL = "localhost"
# Private — leading underscore (convention, not enforced)
_private_var = "internal"
# "Dunder" methods — double underscore
def __init__(self):
pass
# Module-level — use meaningful names, not a, b, c
# ❌
a = 5
b = 10
# ✅
employee_count = 5
department_budget = 10000
Performance Tips
# 1. Use generators for large data
def process_large_file(filepath):
# ❌ Loads entire file into memory
with open(filepath) as f:
lines = f.readlines()
return [line.upper() for line in lines]
# ✅ Processes one line at a time
with open(filepath) as f:
for line in f:
yield line.upper()
# 2. Prefer sets for membership testing (O(1) vs O(n))
data = list(range(1000000))
data_set = set(data)
# ❌ O(n) — slow for large lists
result = 999999 in data # Scans entire list
# ✅ O(1) — constant time
result = 999999 in data_set # Hash lookup
# 3. Use join() for string concatenation in loops
# ❌ Creates new string each iteration — O(n²)
words = ["Hello", "World", "Python"]
result = ""
for word in words:
result += word + " "
# ✅ O(n) — builds once
result = " ".join(words)
# 4. Use local variables inside tight loops
import math
# ❌ Global lookup each iteration
for i in range(1000000):
math.sqrt(i)
# ✅ Local variable lookup is faster
sqrt = math.sqrt
for i in range(1000000):
sqrt(i)
# 5. Measure performance with timeit
import timeit
time = timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
print(f"Time: {time:.4f}s")
17. Comparison with Other Languages
Feature | Python | JavaScript | Java | C++ |
|---|---|---|---|---|
Typing | Dynamic | Dynamic | Static | Static |
Syntax | Very clean | Moderate | Verbose | Complex |
Speed | Slow (interpreted) | Moderate | Fast (JVM) | Very fast |
Indentation | Enforced | Optional | Optional | Optional |
Null concept |
|
|
|
|
String format | f-strings | template literals | String.format | printf |
List/Array |
|
|
|
|
Dict/Object |
|
|
|
|
Multiple return | Tuples natively | Arrays/Objects | Not native | Pairs/Struct |
Error handling | try/except | try/catch | try/catch | try/catch |
Semicolons | Not needed | Needed | Required | Required |
Curly braces | Not used | Required | Required | Required |
# Python
def greet(name):
return f"Hello, {name}!"
print(greet("World"))
// JavaScript equivalent
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("World"));
// Java equivalent
public class Main {
public static String greet(String name) {
return "Hello, " + name + "!";
}
public static void main(String[] args) {
System.out.println(greet("World"));
}
}
When to use Python over alternatives:
Data science, ML, AI — Python is king
Quick scripts and automation — Python is fastest to write
Prototyping — Python's conciseness wins
When you need rich ecosystem of libraries
When NOT to use Python:
Mobile apps — use Swift/Kotlin
High-performance systems — use C++/Rust
Frontend web — use JavaScript
Game development (AAA) — use C++/C#
18. Data Science Perspective
Python is the #1 language for Data Science. Here's how basics connect to DS work:
NumPy Integration
import numpy as np
# Variables and operations apply to arrays too
arr = np.array([1, 2, 3, 4, 5])
print(arr * 2) # [2, 4, 6, 8, 10] — vectorized operation
print(arr[arr > 3]) # [4, 5] — boolean indexing
print(arr.mean()) # 3.0
print(arr.std()) # 1.414...
Pandas Integration
import pandas as pd
# Dict maps directly to DataFrame
data = {
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 30, 35],
"salary": [50000, 70000, 90000]
}
df = pd.DataFrame(data)
# String methods work on Series
df["name"] = df["name"].str.upper()
df["name"] = df["name"].str.strip()
# Loops for data processing
for _, row in df.iterrows():
print(f"{row['name']}: ${row['salary']:,}")
# List comprehension in Pandas
df["bonus"] = [salary * 0.1 for salary in df["salary"]]
# Conditional logic (vectorized)
df["level"] = ["Senior" if age >= 30 else "Junior" for age in df["age"]]
Data Pipeline Example
import json
def etl_pipeline(raw_data):
"""Simple ETL demonstrating Python basics in a DS context."""
# Extract
records = raw_data if isinstance(raw_data, list) else [raw_data]
# Transform
cleaned = []
for record in records:
try:
cleaned.append({
"id": int(record.get("id", 0)),
"name": str(record.get("name", "")).strip().title(),
"score": float(record.get("score", 0)),
"passed": float(record.get("score", 0)) >= 60.0,
"grade": (
"A" if float(record.get("score", 0)) >= 90
else "B" if float(record.get("score", 0)) >= 80
else "C" if float(record.get("score", 0)) >= 70
else "F"
)
})
except (ValueError, TypeError) as e:
print(f"Skipping invalid record {record}: {e}")
# Load (return structured data)
return {
"total": len(cleaned),
"passed": sum(1 for r in cleaned if r["passed"]),
"avg_score": sum(r["score"] for r in cleaned) / len(cleaned) if cleaned else 0,
"records": cleaned
}
# Test
raw = [
{"id": "1", "name": " alice ", "score": "92.5"},
{"id": "2", "name": "BOB", "score": "67.0"},
{"id": "3", "name": "charlie", "score": "abc"}, # Invalid score
]
result = etl_pipeline(raw)
print(json.dumps(result, indent=2))
19. Interview Questions
Basic Level
Q1: What is the difference between is and == in Python?
==compares values.iscompares object identity (memory address). Always useiswhen comparing withNone,True, orFalse.
Q2: What are Python's mutable and immutable data types?
Immutable: int, float, str, tuple, bool, frozenset Mutable: list, dict, set, bytearray Immutable objects can't be changed after creation.
Q3: What is the difference between // and /?
/returns a float (true division).//returns an int (floor division). 7/2 = 3.5, 7//2 = 3.
Q4: What does pass do in Python?
passis a null statement — it does nothing. It's used as a placeholder for future code in loops, functions, or classes.
Q5: How do you swap two variables in Python without a temp variable?
a, b = 10, 20
a, b = b, a # Pythonic swap
Intermediate Level
Q6: What is the difference between append() and extend() in a list?
lst = [1, 2, 3]
lst.append([4, 5]) # [1, 2, 3, [4, 5]] — adds as single element
lst.extend([4, 5]) # [1, 2, 3, 4, 5] — adds each element individually
Q7: What is a list comprehension? Give an example.
A concise way to create lists using a single line of code.
squares = [x**2 for x in range(10) if x % 2 == 0]
Q8: Explain *args and **kwargs.
*argscollects extra positional arguments as a tuple.**kwargscollects extra keyword arguments as a dict.
Q9: What is the difference between copy() and deepcopy()?
copy()creates a shallow copy — nested objects are still referenced.deepcopy()creates a deep copy — all nested objects are also copied.
Q10: What are Python's truthy and falsy values?
Falsy:
None,False,0,0.0,"",[],{},()Truthy: Everything else (any non-zero number, non-empty string, etc.)
Advanced Level
Q11: What is a generator and why use it?
A generator is a function that uses
yieldinstead ofreturn. It produces values lazily (one at a time), saving memory for large datasets.
Q12: What is the GIL in Python?
The Global Interpreter Lock (GIL) allows only one thread to execute Python bytecode at a time. This limits true parallelism in multi-threaded programs. Use
multiprocessingfor CPU-bound tasks instead.
Q13: What is a decorator?
A decorator is a function that takes another function and extends its behavior without modifying it directly. Uses the
@syntax.
Q14: Explain the difference between __str__ and __repr__.
__str__is for human-readable output (used byprint()).__repr__is for unambiguous representation (used byrepr(), shown in REPL).__repr__should ideally return a string that recreates the object.
Scenario-Based
Q15: You have a list of 1 million integers. How do you find duplicates efficiently?
def find_duplicates(lst):
seen = set()
duplicates = set()
for item in lst:
if item in seen:
duplicates.add(item)
else:
seen.add(item)
return duplicates
# O(n) time, O(n) space — much faster than nested loops O(n²)
Q16: Write a one-liner to flatten a 2D list.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
# Or using itertools:
from itertools import chain
flat = list(chain.from_iterable(matrix))
Q17: How would you reverse a string in Python?
s = "Hello, World!"
reversed_s = s[::-1] # "!dlroW ,olleH"
reversed_s = "".join(reversed(s)) # Same result
Q18: What happens when you run a = [] vs a = list()?
Both create an empty list.
[]is a literal and is slightly faster.list()is a constructor call. Prefer[]for performance and clarity.
20. Conclusion
Key Takeaways
You've now covered Python basics from the ground up:
Variables and types: Python is dynamically typed with rich built-in types
Operators: Arithmetic, comparison, logical, membership, identity, bitwise
Strings: Immutable, sliceable, packed with useful methods
Control flow: if/elif/else, match statement, ternary expressions
Loops: for, while, with break/continue/pass/else
Functions: Parameters, return values, lambdas, scope
Data structures: Lists, tuples, dicts, sets — each with their strengths
Comprehensions: Pythonic, concise, fast
Error handling: try/except/finally, custom exceptions
File I/O: Reading, writing, using
with
When to Use What
Need | Use |
|---|---|
Ordered, changeable collection |
|
Fixed/immutable collection |
|
Key-value mapping |
|
Unique elements, fast lookup |
|
One-liner simple function |
|
Large data, memory-efficient | Generator |
File operations |
|
Unknown errors |
|
Final Advice
Write code every day — even 30 minutes makes a massive difference
Read Python's official docs — they're actually very good
Follow PEP 8 — write clean, consistent code from day one
Use the REPL — test small ideas instantly in
pythonoripythonDon't memorize — understand — know the why, the how comes naturally
Build projects — a todo app, a calculator, a data scraper — anything real
Read other people's code — GitHub is your free library
"The best time to learn Python was 5 years ago. The second best time is today."
Happy Coding! 🐍
Written by an experienced Python Developer and Data Scientist. For questions, corrections, or suggestions — drop a comment below.