0% found this document useful (0 votes)
3 views46 pages

Python Complete Notes

This document provides comprehensive notes on Python, covering topics from beginner to advanced levels, including installation, data types, control flow, and data structures. Each chapter includes explanations, examples, and practice problems to reinforce learning. The guide aims to equip learners with the necessary skills to write Python code effectively.

Uploaded by

punitnehra79
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views46 pages

Python Complete Notes

This document provides comprehensive notes on Python, covering topics from beginner to advanced levels, including installation, data types, control flow, and data structures. Each chapter includes explanations, examples, and practice problems to reinforce learning. The guide aims to equip learners with the necessary skills to write Python code effectively.

Uploaded by

punitnehra79
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

PYTHON COMPLETE NOTES

From Beginner to Advanced — With Examples and Practice


Problems

This guide takes you from writing your first line of Python to understanding advanced concepts like decorators,
generators, concurrency, and metaclasses. Every chapter has explanations, runnable examples, and practice
problems.

Page 1
CHAPTER 1: GETTING STARTED

1.1 What is Python?

Python is a high-level, interpreted, general-purpose programming language known for its readable syntax
and huge ecosystem of libraries. It is used in web development, data science, automation, AI/ML, scripting,
and more.

Key features:

• Easy to read and write (uses indentation instead of braces)


• Dynamically typed (no need to declare variable types)
• Interpreted (no separate compile step)
• Huge standard library ("batteries included")
• Cross-platform

1.2 Installing Python

Download from [Link], or use a package manager:


# Windows (using winget)
winget install [Link].3

# macOS (using Homebrew)


brew install python3

# Linux (Debian/Ubuntu)
sudo apt install python3

Check your installed version:


python3 --version

1.3 Running Python Code

There are three common ways to run Python:


# 1. Interactive shell (REPL)
$ python3
>>> print("Hello")

# 2. Running a script file


$ python3 my_script.py

Page 2
# 3. Using Jupyter notebooks / IDEs (VS Code, PyCharm)

1.4 Your First Program

print("Hello, World!")

print() is a built-in function that outputs text to the console.

Practice Problems 1
1. Install Python on your machine and confirm the version using the terminal.
2. Write a program that prints your name, age, and favorite programming language on three separate
lines.
3. Use the REPL to compute 7 * 6 and 2 ** 10.

Page 3
CHAPTER 2: VARIABLES AND DATA TYPES

2.1 Variables

A variable is a name that refers to a value stored in memory. Python variables do not need explicit type
declarations.

name = "Alice"
age = 25
height = 5.6
is_student = True

Naming rules:

• Must start with a letter or underscore


• Can contain letters, numbers, underscores
• Case-sensitive (age and Age are different)
• Cannot be a reserved keyword (if, for, class, etc.)

2.2 Basic Data Types

Type Example Description

int 10, -3 Whole numbers

float 3.14, -0.5 Decimal numbers

str "hello" Text

bool True, False Boolean values

complex 2+3j Complex numbers

NoneType None Absence of a value

x = 10 # int
y = 3.14 # float
name = "Bob" # str
flag = True # bool
z = None # NoneType

print(type(x)) # <class 'int'>

Page 4
2.3 Type Conversion

a = "10"
b = int(a) # string to int
c = float(a) # string to float
d = str(25) # int to string
e = bool(0) # 0 -> False, any nonzero -> True

2.4 Input from the User

name = input("Enter your name: ")


age = int(input("Enter your age: "))
print(f"Hello {name}, you are {age} years old.")

Practice Problems 2
1. Create variables for a product's name, price, and quantity, then print a formatted receipt line.
2. Write a program that asks the user for two numbers and prints their sum, difference, product, and
quotient.
3. What is the output of type(5 / 2) versus type(5 // 2)? Explain the difference.
4. Convert the string "3.14159" to a float and round it to 2 decimal places.

Page 5
CHAPTER 3: OPERATORS

3.1 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
print(a // b) # 3 floor division
print(a % b) # 1 modulus (remainder)
print(a ** b) # 1000 exponentiation

3.2 Comparison Operators

print(5 == 5) # True
print(5 != 3) # True
print(5 > 3) # True
print(5 < 3) # False
print(5 >= 5) # True
print(5 <= 4) # False

3.3 Logical Operators

print(True and False) # False


print(True or False) # True
print(not True) # False

3.4 Assignment Operators

x = 5
x += 3 # x = x + 3 -> 8
x -= 2 # x = x - 2 -> 6
x *= 2 # x = x * 2 -> 12
x /= 4 # x = x / 4 -> 3.0
x //= 1 # floor divide assign
x **= 2 # power assign
x %= 5 # modulus assign

3.5 Identity and Membership Operators

Page 6
a = [1, 2, 3]
b = a
c = [1, 2, 3]

print(a is b) # True (same object)


print(a is c) # False (different objects, same values)
print(a == c) # True (equal values)

print(2 in a) # True
print(5 not in a) # True

Practice Problems 3
1. Write a program to check whether a number is even or odd using the modulus operator.
2. Evaluate 17 // 5, 17 % 5, and 17 ** 2 without running code, then check your answer.
3. Explain the difference between is and == with an example.
4. Write a program that swaps two variables' values without using a third variable.

Page 7
CHAPTER 4: STRINGS

4.1 Creating and Accessing Strings

s = "Hello, Python!"
print(s[0]) # H
print(s[-1]) # !
print(s[0:5]) # Hello (slicing)
print(s[::-1]) # reversed string
print(len(s)) # length of string

4.2 String Methods

s = " Hello World "


print([Link]()) # "Hello World"
print([Link]()) # " hello world "
print([Link]()) # " HELLO WORLD "
print([Link]("World", "Python"))
print([Link]()) # ['Hello', 'World']
print("-".join(["a","b","c"])) # "a-b-c"
print([Link]("World")) # index of substring
print("abc".startswith("a")) # True
print("abc".endswith("c")) # True
print("hello".capitalize()) # "Hello"

4.3 String Formatting

name, age = "Alice", 25

# f-strings (recommended, Python 3.6+)


print(f"{name} is {age} years old")

# .format() method
print("{} is {} years old".format(name, age))

# % operator (older style)


print("%s is %d years old" % (name, age))

# f-string with expressions and formatting


pi = 3.14159265
print(f"Pi rounded: {pi:.2f}")
print(f"{age:>5}") # right align width 5

Page 8
4.4 Strings Are Immutable

s = "hello"
# s[0] = "H" # This raises TypeError!
s = "H" + s[1:] # Create a new string instead

Practice Problems 4
1. Write a function that checks if a given string is a palindrome (ignore case and spaces).
2. Given a sentence, count how many vowels it contains.
3. Write a program to reverse the words in a sentence (not the letters), e.g., "I love Python" -> "Python
love I".
4. Format the number 1234567.891 to display as "1,234,567.89".

Page 9
CHAPTER 5: CONTROL FLOW

5.1 if / elif / else

age = 20
if age < 13:
print("Child")
elif age < 20:
print("Teenager")
else:
print("Adult")

5.2 Ternary (Conditional) Expression

age = 20
status = "Adult" if age >= 18 else "Minor"

5.3 while Loops

count = 0
while count < 5:
print(count)
count += 1
else:
print("Loop finished normally") # runs if no 'break' occurred

5.4 for Loops

for i in range(5): # 0,1,2,3,4


print(i)

for i in range(2, 10, 2): # start, stop, step -> 2,4,6,8


print(i)

fruits = ["apple", "banana", "cherry"]


for fruit in fruits:
print(fruit)

for index, fruit in enumerate(fruits):


print(index, fruit)

Page 10
5.5 break, continue, pass

for i in range(10):
if i == 5:
break # exits the loop
if i % 2 == 0:
continue # skips to next iteration
print(i)

if True:
pass # placeholder, does nothing

5.6 Nested Loops and match-case (Python 3.10+)

for i in range(3):
for j in range(3):
print(i, j)

# Structural pattern matching


command = "start"
match command:
case "start":
print("Starting...")
case "stop":
print("Stopping...")
case _:
print("Unknown command")

Practice Problems 5
1. Print all numbers from 1 to 100 that are divisible by 3 or 5.
2. Write a program that prints the multiplication table of a number entered by the user.
3. Implement FizzBuzz: print numbers 1-100, but print "Fizz" for multiples of 3, "Buzz" for multiples of 5,
and "FizzBuzz" for multiples of both.
4. Use a while loop to find the sum of digits of a given number.

Page 11
CHAPTER 6: DATA STRUCTURES

6.1 Lists

Lists are ordered, mutable collections.

fruits = ["apple", "banana", "cherry"]


[Link]("date") # add to end
[Link](1, "avocado") # insert at index
[Link]("banana") # remove by value
[Link]() # remove & return last item
[Link]() # sort in place
[Link]() # reverse in place
print(len(fruits))
print(fruits[1:3]) # slicing

nested = [[1, 2], [3, 4]]


print(nested[1][0]) # 3

6.2 Tuples

Tuples are ordered and immutable.

point = (3, 4)
x, y = point # unpacking
print(point[0])

single = (5,) # trailing comma needed for a single-element tuple

6.3 Dictionaries

Dictionaries store key-value pairs.

person = {"name": "Alice", "age": 25, "city": "NYC"}


print(person["name"])
person["age"] = 26 # update
person["email"] = "a@[Link]" # add new key
del person["city"] # remove key

for key, value in [Link]():


print(key, value)

print([Link]("phone", "N/A")) # safe access with default


print(list([Link]()))

Page 12
print(list([Link]()))

6.4 Sets

Sets are unordered collections of unique elements.

a = {1, 2, 3}
b = {3, 4, 5}

print(a | b) # union {1,2,3,4,5}


print(a & b) # intersection {3}
print(a - b) # difference {1,2}
print(a ^ b) # symmetric difference {1,2,4,5}

[Link](10)
[Link](1)

6.5 Choosing the Right Structure

Structure Ordered Mutable Duplicates Use case

list Yes Yes Yes General


sequences

tuple Yes No Yes Fixed collections

dict Yes (3.7+) Yes Unique keys Key-value lookups

set No Yes No Unique items,


math ops

Practice Problems 6
1. Given a list of numbers, write code to remove duplicates while preserving order.
2. Merge two dictionaries such that values from the second override the first.
3. Given a list of words, build a dictionary counting how many times each word appears.
4. Write a program to find the common elements between two lists using sets.
5. Create a tuple of coordinates and write a function that computes the distance between two points.

Page 13
CHAPTER 7: COMPREHENSIONS

7.1 List Comprehensions

squares = [x**2 for x in range(10)]


evens = [x for x in range(20) if x % 2 == 0]
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]

7.2 Dictionary and Set Comprehensions

squares_dict = {x: x**2 for x in range(5)}


unique_lengths = {len(word) for word in ["hi", "hello", "hey"]}

7.3 Generator Expressions

gen = (x**2 for x in range(1000000)) # lazy, memory efficient


total = sum(gen)

Practice Problems 7
1. Use a list comprehension to create a list of squares of even numbers from 1 to 20.
2. Flatten a nested list [[1,2],[3,4],[5,6]] into [1,2,3,4,5,6] using a comprehension.
3. Create a dictionary mapping each word in a sentence to its length.
4. Convert a Fahrenheit-to-Celsius conversion into a one-line list comprehension for a list of
temperatures.

Page 14
CHAPTER 8: FUNCTIONS

8.1 Defining Functions

def greet(name):
"""Return a greeting message."""
return f"Hello, {name}!"

print(greet("Alice"))

8.2 Default, Keyword, and Arbitrary Arguments

def power(base, exponent=2):


return base ** exponent

print(power(3)) # 9 (uses default)


print(power(3, 3)) # 27
print(power(exponent=3, base=2)) # keyword args, order doesn't matter

def total(*args, **kwargs):


print(args) # tuple of positional args
print(kwargs) # dict of keyword args

total(1, 2, 3, x=10, y=20)

8.3 Scope

x = 10 # global

def modify():
global x
x = 20

def outer():
y = 5
def inner():
nonlocal y
y += 1
inner()
return y

8.4 Lambda Functions

Page 15
square = lambda x: x**2
add = lambda a, b: a + b
print(square(5)) # 25

# common use: sorting with a key


people = [("Bob", 25), ("Alice", 30)]
[Link](key=lambda p: p[0])

8.5 Recursion

def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)

def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)

8.6 Type Hints (Modern Python)

def add(a: int, b: int) -> int:


return a + b

def greet(name: str, age: int | None = None) -> str:


return f"{name}, age {age}"

Practice Problems 8
1. Write a function is_prime(n) that returns True if n is a prime number.
2. Write a recursive function to compute the nth Fibonacci number, then rewrite it using memoization.
3. Write a function that accepts any number of numeric arguments and returns their average.
4. Write a function apply_discount(price, discount=0.1) and use type hints.
5. Use a lambda function with sorted() to sort a list of dictionaries by a specific key.

Page 16
CHAPTER 9: OBJECT-ORIENTED PROGRAMMING

9.1 Classes and Objects

class Dog:
species = "Canis familiaris" # class attribute

def __init__(self, name, age):


[Link] = name # instance attribute
[Link] = age

def bark(self):
return f"{[Link]} says Woof!"

d = Dog("Rex", 3)
print([Link]())
print([Link])

9.2 Inheritance

class Animal:
def __init__(self, name):
[Link] = name

def speak(self):
raise NotImplementedError

class Cat(Animal):
def speak(self):
return f"{[Link]} says Meow!"

class Dog(Animal):
def speak(self):
return f"{[Link]} says Woof!"

animals = [Cat("Whiskers"), Dog("Rex")]


for a in animals:
print([Link]()) # polymorphism

9.3 Encapsulation

class Account:
def __init__(self, balance):
self._balance = balance # protected (convention)

Page 17
self.__pin = "1234" # private (name-mangled)

@property
def balance(self):
return self._balance

@[Link]
def balance(self, value):
if value < 0:
raise ValueError("Balance cannot be negative")
self._balance = value

acc = Account(100)
[Link] = 200
print([Link])

9.4 Class Methods, Static Methods, Dunder Methods

class Circle:
pi = 3.14159

def __init__(self, radius):


[Link] = radius

def area(self):
return [Link] * [Link] ** 2

@classmethod
def unit_circle(cls):
return cls(1)

@staticmethod
def is_valid_radius(r):
return r > 0

def __str__(self):
return f"Circle(radius={[Link]})"

def __repr__(self):
return f"Circle({[Link]!r})"

def __eq__(self, other):


return [Link] == [Link]

def __add__(self, other):


return Circle([Link] + [Link])

c1 = Circle(5)
print(c1) # uses __str__
print(c1 + Circle(2)) # uses __add__

Page 18
9.5 Abstract Base Classes and Multiple Inheritance

from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def area(self):
pass

class Square(Shape):
def __init__(self, side):
[Link] = side
def area(self):
return [Link] ** 2

class Flyer:
def fly(self):
return "Flying"

class Swimmer:
def swim(self):
return "Swimming"

class Duck(Flyer, Swimmer):


pass

d = Duck()
print([Link](), [Link]()) # multiple inheritance

Practice Problems 9
1. Design a Vehicle base class with subclasses Car and Motorcycle that each override a
describe() method.
2. Create a BankAccount class with deposit(), withdraw(), and a property to prevent negative
balances.
3. Implement __eq__, __lt__, and __str__ for a Point(x, y) class so points can be compared and
sorted.
4. Explain the difference between a classmethod and a staticmethod with an example of each.
5. Create an abstract Shape class with area() and perimeter() methods, then implement
Rectangle and Circle.

Page 19
CHAPTER 10: MODULES AND PACKAGES

10.1 Importing Modules

import math
print([Link](16))

from math import sqrt, pi


print(sqrt(16), pi)

import numpy as np # aliasing

10.2 Creating Your Own Module

# file: [Link]
def greet(name):
return f"Hello {name}"

# file: [Link]
import mymodule
print([Link]("Alice"))

10.3 Packages

A package is a directory containing an __init__.py file and multiple modules.


my_package/
__init__.py
module_a.py
module_b.py

from my_package import module_a

10.4 The if __name__ == "__main__" Idiom

def main():
print("Running as script")

if __name__ == "__main__":
main()

This ensures code only runs when the file is executed directly, not when imported.

Page 20
10.5 Virtual Environments and pip

python3 -m venv myenv # create virtual environment


source myenv/bin/activate # activate (Linux/Mac)
myenv\Scripts\activate # activate (Windows)

pip install requests # install a package


pip freeze > [Link] # save dependencies
pip install -r [Link]

Practice Problems 10
1. Create a module [Link] with functions for area of a circle, square, and triangle, then import
and use it.
2. Explain why the if __name__ == "__main__": guard is useful.
3. Create a virtual environment and install the requests package inside it.
4. Organize three related modules into a package with a proper __init__.py.

Page 21
CHAPTER 11: FILE HANDLING

11.1 Reading and Writing Files

# Writing
with open("[Link]", "w") as f:
[Link]("Hello, file!\n")
[Link](["line1\n", "line2\n"])

# Reading
with open("[Link]", "r") as f:
content = [Link]() # entire file as string

with open("[Link]", "r") as f:


for line in f: # memory-efficient line by line
print([Link]())

with open("[Link]", "a") as f: # append mode


[Link]("more text\n")

The with statement automatically closes the file, even if an error occurs.

11.2 Working with CSV

import csv

with open("[Link]", "w", newline="") as f:


writer = [Link](f)
[Link](["name", "age"])
[Link](["Alice", 25])

with open("[Link]", "r") as f:


reader = [Link](f)
for row in reader:
print(row["name"], row["age"])

11.3 Working with JSON

import json

data = {"name": "Alice", "age": 25, "hobbies": ["reading", "coding"]}

with open("[Link]", "w") as f:


[Link](data, f, indent=2)

Page 22
with open("[Link]", "r") as f:
loaded = [Link](f)

json_string = [Link](data) # to string


parsed = [Link](json_string) # from string

11.4 Working with Paths

from pathlib import Path

p = Path("folder/subfolder/[Link]")
print([Link]) # [Link]
print([Link]) # folder/subfolder
print([Link]) # .txt
print([Link]())
[Link](parents=True, exist_ok=True)

Practice Problems 11
1. Write a program that reads a text file and counts the number of words, lines, and characters.
2. Write student records (name, grade) to a CSV file, then read them back and print the average grade.
3. Save a list of dictionaries as JSON, then load it back and print each entry.
4. Use pathlib to list all .txt files in a directory.

Page 23
CHAPTER 12: EXCEPTION HANDLING

12.1 try / except / else / finally

try:
num = int(input("Enter a number: "))
result = 10 / num
except ValueError:
print("That's not a valid number!")
except ZeroDivisionError:
print("Cannot divide by zero!")
else:
print(f"Result: {result}") # runs if no exception occurred
finally:
print("Execution complete.") # always runs

12.2 Raising Exceptions

def withdraw(balance, amount):


if amount > balance:
raise ValueError("Insufficient funds")
return balance - amount

12.3 Custom Exceptions

class InsufficientFundsError(Exception):
"""Raised when a withdrawal exceeds the balance."""
pass

def withdraw(balance, amount):


if amount > balance:
raise InsufficientFundsError(f"Cannot withdraw {amount}, balance is {balance}")
return balance - amount

try:
withdraw(100, 150)
except InsufficientFundsError as e:
print(e)

12.4 Exception Hierarchy Tips

Page 24
Catch the most specific exception first, then more general ones. Avoid bare except: clauses, since they
hide bugs. Use except Exception as e: if you need a general catch-all.

Practice Problems 12
1. Write a program that repeatedly asks for a number until valid input is given, using try/except.
2. Create a custom exception NegativeValueError and raise it inside a function that computes a
square root.
3. Write a function that safely opens a file and handles FileNotFoundError gracefully.
4. Explain the difference between except Exception and a bare except:.

Page 25
CHAPTER 13: ITERATORS AND GENERATORS

13.1 Iterables and Iterators

nums = [1, 2, 3]
it = iter(nums)
print(next(it)) # 1
print(next(it)) # 2

class Counter:
def __init__(self, limit):
[Link] = limit
self.n = 0
def __iter__(self):
return self
def __next__(self):
if self.n >= [Link]:
raise StopIteration
self.n += 1
return self.n

for x in Counter(3):
print(x) # 1 2 3

13.2 Generators

Generators produce values lazily using yield, which is memory-efficient for large sequences.

def countdown(n):
while n > 0:
yield n
n -= 1

for x in countdown(5):
print(x)

def infinite_evens():
n = 0
while True:
yield n
n += 2

gen = infinite_evens()
print(next(gen), next(gen), next(gen)) # 0 2 4

Page 26
13.3 Generator Expressions and itertools

squares = (x**2 for x in range(10))

import itertools
print(list([Link]([Link](1), 5))) # [1,2,3,4,5]
print(list([Link]([1,2], [3,4]))) # [1,2,3,4]
print(list([Link]([1,2,3], 2)))
print(list([Link]([1,2,3], 2)))

Practice Problems 13
1. Write a generator function that yields the Fibonacci sequence indefinitely.
2. Write a custom iterator class that iterates over even numbers up to a limit.
3. Use [Link] to list all 2-element combinations from ["a","b","c","d"].
4. Explain the memory advantage of a generator expression over a list comprehension for large datasets.

Page 27
CHAPTER 14: DECORATORS

14.1 Functions as First-Class Objects

def shout(text):
return [Link]()

def whisper(text):
return [Link]()

def greet(func):
return func("Hello!")

print(greet(shout)) # HELLO!
print(greet(whisper)) # hello!

14.2 Writing a Decorator

import functools
import time

def timer(func):
@[Link](func)
def wrapper(*args, **kwargs):
start = [Link]()
result = func(*args, **kwargs)
end = [Link]()
print(f"{func.__name__} took {end - start:.4f}s")
return result
return wrapper

@timer
def slow_function():
[Link](1)

slow_function()

14.3 Decorators with Arguments

def repeat(times):
def decorator(func):
@[Link](func)
def wrapper(*args, **kwargs):
for _ in range(times):

Page 28
result = func(*args, **kwargs)
return result
return wrapper
return decorator

@repeat(3)
def say_hi():
print("Hi!")

say_hi() # prints "Hi!" three times

14.4 Built-in Decorators

class MyClass:
@staticmethod
def utility():
return "static"

@classmethod
def create(cls):
return cls()

@property
def value(self):
return self._value

Practice Problems 14
1. Write a decorator @log_calls that prints the function name and arguments every time it's called.
2. Write a decorator that caches results of a slow function (a basic memoization decorator).
3. Write a decorator with an argument that only allows a function to run if a user is "authenticated"
(simulate with a boolean).
4. Explain why [Link] is used inside decorators.

Page 29
CHAPTER 15: CONTEXT MANAGERS

15.1 Using with Statements

with open("[Link]") as f:
data = [Link]()
# file automatically closed here

15.2 Writing a Custom Context Manager (Class-Based)

class Timer:
def __enter__(self):
import time
[Link] = [Link]()
return self

def __exit__(self, exc_type, exc_value, traceback):


import time
print(f"Elapsed: {[Link]() - [Link]:.2f}s")
return False # propagate exceptions if any

with Timer():
sum(range(1000000))

15.3 Writing a Context Manager with contextlib

from contextlib import contextmanager

@contextmanager
def open_resource(name):
print(f"Opening {name}")
yield name
print(f"Closing {name}")

with open_resource("database") as res:


print(f"Using {res}")

Practice Problems 15
1. Write a context manager that temporarily changes the working directory and restores it afterward.
2. Write a context manager using @contextmanager that suppresses a specific exception type.
3. Explain what __enter__ and __exit__ are responsible for.

Page 30
Page 31
CHAPTER 16: FUNCTIONAL PROGRAMMING

16.1 map, filter, reduce

nums = [1, 2, 3, 4, 5]

squared = list(map(lambda x: x**2, nums))


evens = list(filter(lambda x: x % 2 == 0, nums))

from functools import reduce


total = reduce(lambda a, b: a + b, nums) # 15

16.2 functools Utilities

from functools import partial, lru_cache

def power(base, exp):


return base ** exp

square = partial(power, exp=2)


print(square(5)) # 25

@lru_cache(maxsize=None)
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)

16.3 Closures

def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5), triple(5)) # 10 15

Practice Problems 16
1. Use map() and filter() together to get the squares of only the odd numbers in a list.
2. Use [Link]() to find the maximum value in a list without using max().

Page 32
3. Write a closure-based counter function that remembers its count between calls.
4. Use lru_cache to speed up a recursive Fibonacci function and measure the difference.

Page 33
CHAPTER 17: REGULAR EXPRESSIONS

17.1 Basics with re

import re

text = "My phone number is 123-456-7890"


match = [Link](r"\d{3}-\d{3}-\d{4}", text)
if match:
print([Link]()) # 123-456-7890

emails = "Contact: a@[Link], b@[Link]"


found = [Link](r"[\w.]+@[\w.]+", emails)
print(found)

cleaned = [Link](r"\s+", " ", "too many spaces")


print(cleaned) # "too many spaces"

parts = [Link](r",\s*", "a, b,c, d")


print(parts) # ['a', 'b', 'c', 'd']

17.2 Common Patterns

Pattern Meaning

\d digit

\w word character

\s whitespace

+ one or more

* zero or more

? zero or one

^ start of string

$ end of string

(...) capturing group

Practice Problems 17
1. Write a regex to validate whether a string is a valid email address.

Page 34
2. Extract all hashtags (e.g., "#python") from a block of text.
3. Write a regex to check if a password contains at least one uppercase, one lowercase, one digit, and is
8+ characters.
4. Use [Link] to mask all digits in a string of credit card numbers with "*".

Page 35
CHAPTER 18: WORKING WITH DATES AND TIME

from datetime import datetime, timedelta

now = [Link]()
print([Link]("%Y-%m-%d %H:%M:%S"))

future = now + timedelta(days=7)


print(future)

d1 = datetime(2024, 1, 1)
d2 = datetime(2024, 12, 31)
print((d2 - d1).days) # difference in days

parsed = [Link]("2024-05-01", "%Y-%m-%d")

Practice Problems 18
1. Write a program that calculates someone's age given their birth date.
2. Write a function that returns the date 30 days from today.
3. Parse the string "15/08/2024" into a datetime object.

Page 36
CHAPTER 19: CONCURRENCY — THREADING,
MULTIPROCESSING, ASYNCIO

19.1 Threading (I/O-bound tasks)

import threading
import time

def worker(n):
[Link](1)
print(f"Worker {n} done")

threads = []
for i in range(5):
t = [Link](target=worker, args=(i,))
[Link](t)
[Link]()

for t in threads:
[Link]()

Note: Python's Global Interpreter Lock (GIL) means threads don't achieve true CPU parallelism, but they
work well for I/O-bound tasks (network calls, file I/O).

19.2 Multiprocessing (CPU-bound tasks)

from multiprocessing import Pool

def square(n):
return n * n

if __name__ == "__main__":
with Pool(4) as p:
results = [Link](square, range(10))
print(results)

Multiprocessing creates separate processes, bypassing the GIL, and is suited for CPU-heavy work.

19.3 Asyncio (Asynchronous I/O)

import asyncio

async def fetch_data(n):

Page 37
print(f"Start {n}")
await [Link](1)
print(f"Done {n}")
return n * 2

async def main():


results = await [Link](*(fetch_data(i) for i in range(3)))
print(results)

[Link](main())

Practice Problems 19
1. Explain the difference between threading, multiprocessing, and asyncio, and when you'd use each.
2. Write a multithreaded program that downloads (simulate with sleep) 5 "files" concurrently.
3. Write an asyncio program that runs 3 asynchronous tasks concurrently and prints total time taken.
4. Use multiprocessing to compute the square of numbers 1-1,000,000 faster than a plain loop.

Page 38
CHAPTER 20: TESTING

20.1 Writing Tests with unittest

import unittest

def add(a, b):


return a + b

class TestMath([Link]):
def test_add_positive(self):
[Link](add(2, 3), 5)

def test_add_negative(self):
[Link](add(-1, -1), -2)

if __name__ == "__main__":
[Link]()

20.2 Writing Tests with pytest (more common today)

# file: test_math.py
def add(a, b):
return a + b

def test_add():
assert add(2, 3) == 5

def test_add_negative():
assert add(-1, -1) == -2

$ pytest test_math.py -v

20.3 Fixtures and Parametrization (pytest)

import pytest

@[Link]
def sample_data():
return [1, 2, 3, 4, 5]

def test_sum(sample_data):
assert sum(sample_data) == 15

Page 39
@[Link]("a,b,expected", [(1,2,3), (0,0,0), (-1,1,0)])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected

Practice Problems 20
1. Write unit tests for a Calculator class covering add, subtract, multiply, and divide (including division
by zero).
2. Convert your unittest tests into pytest-style tests.
3. Write a parametrized pytest test for a is_prime() function using at least 5 cases.

Page 40
CHAPTER 21: ADVANCED TOPICS

21.1 Descriptors

class PositiveNumber:
def __set_name__(self, owner, name):
[Link] = "_" + name

def __get__(self, obj, objtype=None):


return getattr(obj, [Link])

def __set__(self, obj, value):


if value < 0:
raise ValueError("Must be positive")
setattr(obj, [Link], value)

class Product:
price = PositiveNumber()
def __init__(self, price):
[Link] = price

21.2 Metaclasses

class Meta(type):
def __new__(mcs, name, bases, namespace):
namespace["created_by"] = "Meta"
return super().__new__(mcs, name, bases, namespace)

class MyClass(metaclass=Meta):
pass

print(MyClass.created_by) # "Meta"

Metaclasses control how classes themselves are created. They are rarely needed in everyday code but
power frameworks like Django's ORM.

21.3 Data Classes

from dataclasses import dataclass, field

@dataclass
class Point:
x: float
y: float

Page 41
tags: list = field(default_factory=list)

def distance_from_origin(self):
return (self.x**2 + self.y**2) ** 0.5

p = Point(3, 4)
print(p) # auto-generated __repr__
print(p.distance_from_origin())

21.4 Type Hinting and Static Analysis

from typing import List, Dict, Optional, Union, Callable

def process(items: List[int]) -> Dict[str, int]:


return {"count": len(items), "sum": sum(items)}

def find_user(id: int) -> Optional[str]:


return None

Callback = Callable[[int, int], int]


def apply(func: Callback, a: int, b: int) -> int:
return func(a, b)

Run mypy to statically check type hints for errors before runtime.

21.5 Memory Management and the GIL

Python uses reference counting plus a cyclic garbage collector to manage memory. The Global Interpreter
Lock (GIL) ensures only one thread executes Python bytecode at a time, which simplifies memory
management but limits CPU-bound multithreading (use multiprocessing instead).

import sys
x = [1, 2, 3]
print([Link](x))

import gc
[Link]() # force garbage collection

21.6 Working with APIs (requests)

import requests

response = [Link]("[Link]
if response.status_code == 200:
data = [Link]()
print(data["login"])

Page 42
payload = {"name": "Alice"}
response = [Link]("[Link] json=payload)

21.7 Packaging a Project

my_project/
src/
my_project/
__init__.py
[Link]
tests/
test_core.py
[Link]
[Link]

# [Link] (minimal example)


[project]
name = "my_project"
version = "0.1.0"
dependencies = ["requests"]

Practice Problems 21
1. Create a @dataclass called Book with title, author, and price fields, and a method to apply a
discount.
2. Write a descriptor that ensures a string attribute is always stored in uppercase.
3. Add type hints to three functions you wrote in earlier chapters and run mypy to check them.
4. Use the requests library to fetch data from a public API and print a formatted summary.
5. Explain in your own words what the GIL is and why multiprocessing bypasses it.

Page 43
APPENDIX A: PYTHON STYLE (PEP 8) QUICK
REFERENCE

• Use 4 spaces per indentation level (never tabs).


• Limit lines to 79-99 characters.
• Use snake_case for variables and functions, PascalCase for classes, UPPER_CASE for constants.
• Two blank lines before top-level function/class definitions.
• Use docstrings ("""...""") to document modules, classes, and functions.
• Prefer f-strings over % or .format() for readability.
• Use is / is not when comparing to None.

Page 44
APPENDIX B: COMMON BUILT-IN FUNCTIONS
CHEAT SHEET

Function Purpose

len() length of a sequence

range() sequence of numbers

enumerate() index + value pairs

zip() combine iterables

sorted() return a new sorted list

map() / filter() transform / filter iterables

sum(), min(), max() aggregate functions

any(), all() boolean aggregation

isinstance() type checking

dir() list attributes of an object

help() show documentation

names = ["Bob", "Alice", "Eve"]


ages = [25, 30, 22]
for name, age in zip(names, ages):
print(name, age)

print(sorted(names))
print(any(age > 28 for age in ages))
print(all(age > 18 for age in ages))

Page 45
APPENDIX C: NEXT STEPS

Once comfortable with this material, continue with:

• Data science: NumPy, pandas, Matplotlib


• Web development: Flask, FastAPI, Django
• Automation: scripting, Selenium, BeautifulSoup
• Testing and CI/CD: pytest, GitHub Actions
• Contributing to open-source projects on GitHub

Practice consistently — build small projects (a to-do app, a web scraper, a simple API) to reinforce every
concept in these notes.

Page 46

You might also like