Back to all posts

The Ultimate Python Basics Guide: From Zero to Confident Developer

Master Python basics from scratch — variables, data types, operators, strings, loops, functions, and more. A complete beginner-to-advanced guide with real-wo...

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:

Python
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

Bash
# 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

Bash
# 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

Python
# hello.py
print("Hello, World!")

Output:

SQL
Hello, World!

That's it. No semicolons. No curly braces. No public static void main. Just pure simplicity.


3. Basic Concepts

Comments

Python
# 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

Python
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.

Python
# 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.

Python
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.

Python
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

Python
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

Python
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?

int

42, -5, 1_000_000

No

float

3.14, -0.001, 1.5e10

No

complex

3+4j

No

str

"Hello", 'World'

No

bool

True, False

No

list

[1, 2, 3]

Yes

tuple

(1, 2, 3)

No

set

{1, 2, 3}

Yes

frozenset

frozenset({1, 2})

No

dict

{"key": "value"}

Yes

bytes

b"hello"

No

bytearray

bytearray(5)

Yes

NoneType

None

No

Multiple Assignment

Python
# 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.

Python
MAX_CONNECTIONS = 5000
PI = 3.14159
DATABASE_URL = "postgresql://localhost/mydb"
API_KEY = "abc123"       # Never hardcode in production!

Type Conversion (Casting)

Python
# 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

Python
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

Python
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

Python
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

Python
# 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

Python
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

Python
# 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

Python
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

Python
# 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

Python
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

\n

Newline

\t

Tab

\\

Backslash

\"

Double quote

\'

Single quote

\r

Carriage return

\0

Null character

\uXXXX

Unicode character

\b

Backspace

String Indexing and Slicing

Python
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

Python
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

Python
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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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.

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
# 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

Python
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

Python
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

Python
# 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)

Python
# 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

Python
# 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 tries

  • try/except ValueError — gracefully handles non-integer inputs

  • continue — skips to next iteration if input was invalid (doesn't count as attempt)

  • attempts += 1 — increment only after valid input

  • return True/False — function signals win or loss to caller


Example 2: Student Grade Calculator

Python
# 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

Python
# 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

Python
# ❌ 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

NameError

Variable used before assignment

Check spelling or define variable first

TypeError

Wrong type operation

Cast with int(), str(), float()

IndexError

List index out of range

Check list length, use .get() for dicts

KeyError

Dict key doesn't exist

Use .get(key, default)

ValueError

Wrong value for type

Use try/except ValueError

AttributeError

Method doesn't exist on object

Check the type with type()

IndentationError

Wrong indentation

Use 4 spaces consistently

ZeroDivisionError

Dividing by zero

Check denominator before dividing

RecursionError

Infinite recursion

Add base case to recursive function

StopIteration

Iterator exhausted

Use try/except StopIteration or for loop


16. Pro Developer Insights

Best Practices

Python
# 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)

Python
# 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

Python
# 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

None

null/undefined

null

nullptr

String format

f-strings

template literals

String.format

printf

List/Array

list

Array

ArrayList

vector

Dict/Object

dict

Object

HashMap

unordered_map

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
# Python
def greet(name):
    return f"Hello, {name}!"

print(greet("World"))
JavaScript
// JavaScript equivalent
function greet(name) {
    return `Hello, ${name}!`;
}
console.log(greet("World"));
Java
// 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

Python
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

Python
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

Python
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. is compares object identity (memory address). Always use is when comparing with None, True, or False.

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?

pass is 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?

Python
a, b = 10, 20
a, b = b, a    # Pythonic swap

Intermediate Level

Q6: What is the difference between append() and extend() in a list?

Python
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.

Python
squares = [x**2 for x in range(10) if x % 2 == 0]

Q8: Explain *args and **kwargs.

*args collects extra positional arguments as a tuple. **kwargs collects 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 yield instead of return. 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 multiprocessing for 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 by print()). __repr__ is for unambiguous representation (used by repr(), 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?

Python
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.

Python
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?

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

list

Fixed/immutable collection

tuple

Key-value mapping

dict

Unique elements, fast lookup

set

One-liner simple function

lambda

Large data, memory-efficient

Generator

File operations

with open(...)

Unknown errors

try/except Exception

Final Advice

  1. Write code every day — even 30 minutes makes a massive difference

  2. Read Python's official docs — they're actually very good

  3. Follow PEP 8 — write clean, consistent code from day one

  4. Use the REPL — test small ideas instantly in python or ipython

  5. Don't memorize — understand — know the why, the how comes naturally

  6. Build projects — a todo app, a calculator, a data scraper — anything real

  7. 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.

0 likes

Rate this post

No rating

Tap a star to rate

0 comments

Latest comments

0 comments

No comments yet.

Keep building your data skillset

Explore more SQL, Python, analytics, and engineering tutorials.