🐍 Python Beginner Notes
✅ Core Data Types
1. int (Integer)
o Any whole number without a decimal.
o Examples: 2, 3, -9, 3700, -102.
o As long as no decimal is present, it is considered an
integer.
2. float
o Any number with a decimal point.
o Examples: 2.0, 9.7, 0.0, -3.14.
o Even 2.0 is a float due to the .0.
3. str (String)
o A sequence of characters/numbers inside quotes.
o Can use either ' ' or " " — both are equivalent.
o Examples:
"hello", 'hello', "4.6"
"4.6" is a string, not a float, because it's
quoted.
o To include quotes inside a string:
'\"Hello\"' → Wrap in single quotes.
"\'World\'" → Wrap in double quotes.
4. bool (Boolean)
o Represents True or False.
o Can be seen as 1 (True) or 0 (False).
o Examples: True, False.
Output and Printing
print() Function
Used to display output to the console.
Example:
print("Hello, world")
Printing Rules:
Strings must be inside quotes.
Numbers like 4.5 can be printed directly (recognized as float).
Print Multiple Values:
print(4.5, "hello", False)
# Output: 4.5 hello False
Custom end Parameter:
By default, print() ends with a newline (\n).
You can change it:
print("Hello", end="|")
print("World")
# Output: Hello|World
🔤 Variables
Assigning Variables:
x = 10
name = "Tim"
Printing Variables:
print(x) # Output: 10
print(name) # Output: Tim
Dynamic Typing & Multiple Assignments:
n=0 # int
n = "abc" # str
n, m = 0, "abc"
n, m, z = 0.125, "abc", False
Increment:
n += 1 # Correct
n++ # ❌ Invalid in Python
Variable Naming Rules:
Must start with a letter or underscore.
Cannot start with a number.
Cannot contain special characters except _.
Valid: hello_world, _var32, num1.
Invalid: 9hello, hello$, #name.
Naming Conventions:
Use snake_case (e.g., hello_world).
Avoid camelCase (common in Java/JS).
⌨️User Input
Getting Input:
input("Prompt: ") # Prompt must be a string.
Store Input in Variable:
name = input("Name: ")
print(name)
Input is Always a String:
age = input("Age: ") # "25" (string)
print("You are", age, "years old")
Convert Input to Number:
num = int(input("Enter a number: "))
print(num - 5)
➕ Arithmetic Operators
Symbo
Operator Description
l
Addition + 9 + 3 → 12
Subtraction - 9-3→6
Multiplication * 9 * 3 → 27
Division / Always returns float (9 / 3 → 3.0)
Floor Division // Returns integer (10 // 3 → 3)
Modulus % Remainder (10 % 3 → 1)
Exponentiation ** Power (3 ** 2 → 9)
Note: Mixed int and float operations result in float.
📌 Data Type Conversion
String to Integer:
num = int(input("Enter a number: "))
print(num - 5)
String to Float:
f = float(input("Enter a float: "))
print(f * 2)
➗ Order of Operations (BEDMAS/PEMDAS)
1. Brackets
2. Exponents
3. Division & Multiplication (left to right)
4. Addition & Subtraction (left to right)
Example:
result = (2 + 3) * 5 ** 2 # Output: 125
⚠️Common Errors
Mixing incompatible types:
"hello" + 9 # ❌ Error
Math with string inputs without conversion.
📚 String Methods & Concepts
🔹 What is a Method?
A method is a function associated with an object.
Called using dot notation: object.method().
Example:
"hello".upper() # Makes the string uppercase.
🔹 type() Function
Checks the data type of a variable.
Example:
type("hello") # Returns: <class 'str'>
🔹 Common String Methods
Method Description Example Output
Converts all characters to
.upper() "Hello".upper() "HELLO"
uppercase.
Converts all characters to
.lower() "Hello".lower() "hello"
lowercase.
Capitalizes the first
.capitalize() "hello".capitalize() "Hello"
character; rest lowercase.
Counts occurrences of x in
.count(x) "Hello".count("l") 2
the string.
Key Notes:
Case Sensitivity: Methods are case-sensitive.
"Hello".count("L") # 0 (no uppercase 'L')
"Hello".lower().count("l") # 2 (converted to lowercase)
🔹 Method Chaining
Combine multiple methods in one line.
Example:
"Hello World".lower().count("o") # Output: 2
s = "abc"
# Immutable: s[0] = "A" ❌
s += "def" # New string "abcdef"
# ASCII values
print(ord("a")) # 97
# Join list of strings
print(" ".join(["hello", "world"])) # "hello world"
🧮 String Operations
🔹 Concatenation (+)
Combines strings:
"Hello" + "World" # Output: "HelloWorld"
🔹 Repetition (*)
Repeats a string:
"Hi" * 3 # Output: "HiHiHi"
🔍 Conditional Operators (Comparisons)
Comparison Operators
Operato
Meaning Example Result
r
== Equal to "a" == "a" True
!= Not equal to "a" != "b" True
< Less than (ASCII comparison) "a" < "b" True
> Greater than "Z" > "a" False
Math Operations
import math
# Division rounding
print(-3 // 2) # -2 (rounds down)
print(int(-3 / 2)) # -1 (rounds toward zero)
# Modulo quirks
print(-10 % 3) # 2 (Python)
print(math.fmod(-10, 3)) # -1.0 (like C++)
# Math utilities
print(math.floor(3 / 2)) # 1
print(math.pow(2, 3)) # 8.0
print(float("inf") > 1e9) # True
String Comparison Rules
Compares character-by-character using ASCII values:
ord("A") # 65
ord("a") # 97
"a" > "Z" # True (97 > 65)
🔗 Chained Conditionals & Logical Operators
🔹 Boolean Logic
Operato
Description Example Result
r
and True only if both sides are True. True and False False
or True if at least one side is True. True or False True
not Reverses the Boolean value. not True False
Operator Precedence
1. not
2. and
3. or
Examples:
print(True and False) # False
print(not (False or True)) # False
🧠 If / Elif / Else Statements
🔹 Basic Syntax
if condition:
# Code block
elif condition:
# Code block
else:
# Code block
🔹 Key Rules
Only one if and one else allowed.
Multiple elif blocks can be used.
else must come last.
Example:
name = "Tim"
if name == "Tim":
print("You are great")
elif name == "Joe":
print("Bye Joe")
else:
print("No match")
Nested Conditions
if x > 2:
if y < 5:
print("Nested condition")
📘 Python Beginner Notes: Lists and
Tuples
📌 What is a Collection?
A collection is a group of items/elements.
Python has various
collections: lists, tuples, sets, dictionaries.
Focus in this section: Lists and Tuples.
🔹 Lists
✅ What is a List?
A list is a collection that is:
o Ordered (maintains the order of elements).
o Mutable (can be changed).
o Can hold multiple data types (integers, strings,
booleans, etc.).
🔹 Example:
x = [4, True, "hi"]
✅ Creating a List
x = [4, True, "hi"] # List with elements
x = [] # Empty list
🔧 List Operations
🧮 len() function
Returns the number of elements in the list (or string, etc.).
len(x) # → 3
➕ append()
Adds a single element to the end of the list.
x.append("hello") # x becomes [4, True, "hi", "hello"]
➕ extend()
Adds multiple elements from another list.
x.extend([4, 5, 6]) # Appends 4, 5, 6 to the end
➖ pop()
Removes and returns the last element if no index is given.
x.pop() # Removes last item
x.pop(0) # Removes item at index 0 (first element)
🔎 Accessing Elements
Use square brackets [] with an index.
Indexing starts at 0.
x[0] # First element
x[2] # Third element
x[-1] # Last element
✏️Modifying Elements
x[0] = "hello" # Changes value at index 0 to "hello"
🔁 List Mutability
Lists are mutable.
If you assign one list to another (y = x), both variables point to
the same object (reference).
Changes to one will reflect in the other.
x = [1, 2, 3]
y=x
x[0] = 100
print(y) # → [100, 2, 3]
✅ Creating a Copy of a List (not reference):
y = x[:] # This creates a shallow copy
Now changes in x won't affect y.
🔀 Lists Can Be Nested
Lists can contain other lists or tuples.
nested_list = [[1, 2], [3, 4]]
🧮 Stack Operations
arr = [1, 2, 3]
arr.append(4) # [1, 2, 3, 4]
arr.pop() # [1, 2, 3]
🔪 Sublists (Slicing)
print(arr[1:3]) # [2, 3] (last index exclusive)
print(arr[0:10]) # No out-of-bounds error!
📦 Unpacking
a, b, c = [1, 2, 3] # a=1, b=2, c=3
🔀 Custom Sort
arr = ["bob", "alice", "jane"]
arr.sort(key=lambda x: len(x)) # Sort by length
📊 2D List (Correct Way)
matrix = [[0] * 4 for _ in range(4)] # 4x4 matrix
🔹 Tuples
✅ What is a Tuple?
A tuple is similar to a list but:
o Immutable (cannot be changed after creation).
o Ordered.
🔹 Syntax:
x = (1, 2, 3)
🔎 Accessing Elements (Same as list):
x[0] # → 1
🔒 Tuples are Immutable
Cannot:
o Modify: x[0] = 5 → ❌ Error
o Append: x.append(4) → ❌ Error
o Pop: x.pop() → ❌ Error
❗ You must redefine the tuple to change it.
tup = (1, 2, 3)
# Immutable: tup[0] = 0 ❌
🔑 As Dictionary Keys
myMap = {(1, 2): "value"} # Valid (tuples are hashable)
💡 Tuple Summary
Tuples are used when data should not change.
Safer for fixed collections.
Slightly more memory-efficient than lists.
🔗 Mixed and Nested Collections
Python allows nesting and mixing types:
x = [1, (2, 3), [4, 5]]
You can put:
o Tuples inside lists
o Lists inside tuples
o Lists inside lists
o Tuples inside tuples
✅ Summary Table: List vs Tuple
Feature List Tuple
Syntax [1, 2, 3] (1, 2, 3)
Mutabilit
Mutable (can change) Immutable (cannot change)
y
Ordered ✅ Yes ✅ Yes
Methods .append(), .pop() etc. ❌ (None for modifying)
Use Case Dynamic data Static data
🔁 Python Loops: for and while
🔹 FOR LOOP
✅ Purpose
Used when you know in advance how many times you want
to iterate.
Often used to iterate over:
o A range of numbers.
o Collections (lists, strings, tuples).
✅ Basic Syntax
for variable in iterable:
# do something
🔹 Example
for i in range(10):
print(i)
# Output: 0 to 9 (10 not included)
🔄 Reverse Loops
for i in range(5, 1, -1): # 5, 4, 3, 2
print(i)
🔹 range() Function
Used to generate a sequence of numbers.
🔧 Syntax
range(start, stop, step)
Argument Description Default
start Starting number 0
stop Up to but not including this number Required
Argument Description Default
step Increment between numbers 1
🔹 Examples
range(10) # 0 to 9
range(1, 10) # 1 to 9
range(1, 10, 2) # 1, 3, 5, 7, 9
range(10, -1, -1) # 10 to 0 (reverse)
⚠️Invalid Range
range(-10, -1, -1) # Does nothing (already past stop condition)
🔁 Looping Through Lists
🔹 Example
x = [3, 4, 5, 6]
for i in x:
print(i)
# Prints all elements in the list.
📍 Accessing by Index with range(len(...))
x = [3, 4, 5, 6]
for i in range(len(x)):
print(x[i])
# Iterates using indexes: 0 to len(x)-1.
🔢 enumerate() Function
Used to access both index and value at the same time.
x = [3, 4, 5, 6]
for i, element in enumerate(x):
print(i, element)
Output:
03
14
25
36
🔁 Summary: for Loop Variants
Style Use Case
for i in range(n) Fixed number of times
for val in list Loop through elements
for i in range(len(x)) Index-based access
for i, val in enumerate(x) Index + value access
🔄 WHILE LOOP
✅ Purpose
Used when you want to run a loop based on a
condition rather than a set number of times.
✅ Basic Syntax
while condition:
# do something
🔹 Example
i=0
while i < 10:
print("Current run")
i += 1
# Runs until the condition (i < 10) becomes False.
🔁 Increment Variants
i += 1 # Add 1
i *= 2 # Multiply by 2
i /= 2 # Divide by 2
🧨 Infinite Loop with break
🔹 Example
i=0
while True:
i += 1
if i == 10:
break
Runs infinitely until i == 10, then breaks out of the loop.
🔄 Nested Loop Note
break only exits the innermost loop.
🔁 Loop Type Comparison
Feature for Loop while Loop
When number of iterations is When looping until a condition
Use Case
known fails
Iterating over collections or
Typical Use Waiting for a condition to fail
ranges
Break on
Yes (using break) Yes
Condition?
Infinite Loop
Rare Common (with while True)
Setup
🔪 Python Slice Operator
✅ What is a Slice?
A subset of a sequence (lists, strings, tuples)
Created using square brackets [] with colons :
Syntax mirrors range() function
📚 General Syntax
python
Copy
Download
sequence[start : stop : step]
Component Meaning Default
start Index to start (inclusive) 0
stop Index to stop (exclusive) len(seq)
step Step size (positive/negative) 1
🔹 Basic Example
x = [0, 1, 2, 3, 4, 5]
x[0:4:2] # Output: [0, 2] (start at 0, stop before 4, step 2)
✅ Partial Slicing
Syntax Meaning Example
x[:4] Start → index 4 [0,1,2,3]
x[2:] Index 2 → end [2,3,4,5]
x[::2] Whole list, step 2 [0,2,4]
x[4:2:-1] Backward from 4 to 2 (exclusive) [4,3]
🔄 Reversing Sequences
x[::-1] # Reverses list → [5,4,3,2,1,0]
s = "hello"
s[::-1] # "olleh"
🔹 String/Tuple Slicing
t = (0,1,2,3,4)
t[1:4:2] # (1,3)
s = "python"
s[1:4] # "yth"
💡 Slice Summary Table
Syntax Description Example Output
x[start:stop] From start to stop-1 x[1:4] → [1,2,3]
x[:stop] Start → stop-1 x[:3] → [0,1,2]
x[start:] Start → end x[2:] → [2,3,4,5]
x[::step] Full sequence with step x[::2] → [0,2,4]
x[::-1] Reversed sequence x[::-1] → [5,4,...,0]
🟩 Python Sets
✅ What is a Set?
Unordered, unique collection
No indexing (cannot access by position)
Extremely fast membership tests
🧱 Creating Sets
s = set() # Empty set (NOT {})
s = {4, 30, 2, 2} # {2, 4, 30} (duplicates auto-removed)
🛠 Core Operations
Operation Method Example
Add element .add() s.add(5)
Remove s.remove(5) (KeyError if
.remove()
element missing)
Check
in operator 4 in s → True
membership
Union .union() or ` ` s1.union(s2)
Intersection .intersection() or & s1 & s2
Difference .difference() or - s1 - s2
🔹 Set Methods Deep Dive
s1 = {1, 2, 3}
s2 = {3, 4, 5}
s1.union(s2) # {1,2,3,4,5}
s1.intersection(s2) # {3}
s1.difference(s2) # {1,2}
s1.symmetric_difference(s2) # {1,2,4,5} (XOR)
⚡ Performance Notes
Membership test x in set → O(1) (vs O(n) for lists)
Ideal for duplicate removal:
list(set([1,2,2,3])) # [1,2,3]
🔀 Set Comprehension
{i for i in range(5)} # {0,1,2,3,4}
📌 Key Comparisons
Slice Operator
Works on lists, strings, tuples
Never modifies original (returns new object)
Negative indices supported (x[-3:] → last 3 elements)
Sets
Cannot store lists/dicts (only hashable types)
No order → print({1,2,3} may show {2,1,3}
Frozen sets exist for immutable versions
⚠️Common Pitfalls
{} # Creates dict, not set!
set([[]]) # TypeError: unhashable type
x[1:10] # No error even if sequence shorter than 10
Both features are essential for clean, Pythonic code - slices for
sequence manipulation and sets for efficient uniqueness operations.
🟨 Python Dictionaries
✅ What is a Dictionary?
Key-value pair collection (similar to hash maps/objects)
Keys must be immutable (strings, numbers, tuples)
Values can be any type
Implemented with hash tables for O(1) lookups
🧱 Creating Dictionaries
x = {"key": 4} # Basic
x = dict(key1=5, key2=6) # Alternate syntax
empty_dict = {} # Empty dictionary
🔍 Core Operations
Operation Syntax Example
Add/Modify dict[key] = value x["new_key"] = 10
Operation Syntax Example
Access dict[key] x["key"] → 4
Check Key Existence key in dict "key" in x → True
Delete Key del dict[key] del x["key"]
Get All Keys .keys() list(x.keys()) → ["key"]
Get All Values .values() list(x.values()) → [4]
Get Key-Value Pairs .items() list(x.items()) → [("key",4)]
🔁 Iterating Over Dictionaries
# Method 1: Key-value pairs
for key, value in x.items():
print(key, value)
# Method 2: Keys only
for key in x:
print(key, x[key])
# Method 3: Values only
for value in x.values():
print(value)
⚡ Dictionary Comprehension
{key_expr: value_expr for item in iterable}
Examples:
{i: i*2 for i in range(3)} # {0:0, 1:2, 2:4}
{k.lower(): v for k,v in {"A":1}.items()} # {"a":1}
⚠️Common Pitfalls
x["missing_key"] # KeyError
x.get("missing_key") # Returns None (safe)
x.get("key", default) # Returns default if missing
🟩 Python Comprehensions
✅ What is a Comprehension?
One-line syntax for creating collections
More readable and often faster than loops
Supported for: lists, dicts, sets, generators
📋 List Comprehension
[expression for item in iterable]
Examples:
[x*2 for x in range(5)] # [0,2,4,6,8]
[x for x in "hello" if x in "aeiou"] # ["e","o"]
📚 Dictionary Comprehension
{key: value for item in iterable}
Example:
{i: str(i) for i in [1,2,3]} # {1:"1", 2:"2", 3:"3"}
🔷 Set Comprehension
{expression for item in iterable}
Example:
{ord(c) for c in "apple"} # {97, 112, 108, 101}
🟣 Generator Expression
(expression for item in iterable)
sum(x*x for x in range(10)) # 285 (memory efficient)
🧠 Advanced Features
Nested Comprehensions
[[i*j for j in range(3)] for i in range(2)]
# [[0,0,0], [0,1,2]]
Conditional Logic
["Even" if x%2==0 else "Odd" for x in range(4)]
# ["Even", "Odd", "Even", "Odd"]
Multiple Iterables
[x+y for x in "abc" for y in "123"]
# ["a1","a2","a3","b1",...,"c3"]
📌 Performance Notes
Faster than equivalent for loops
More memory efficient than manual appending
Avoid nested comprehensions beyond 2 levels for readability
🔑 Key Comparisons
Feature Dictionaries Comprehensions
Primary Use Key-value storage Collection creation
Mutability Mutable Creates new object
Lookup Speed O(1) for keys N/A
Best For Structured data, configurations Transforming/filtering data
Memory
Higher overhead Efficient for one-time operations
Usage
Both dictionaries and comprehensions are essential Python tools -
dictionaries for structured data storage and comprehensions for
elegant data transformations.
Heaps
import heapq
# Min-heap (default)
minHeap = []
heapq.heappush(minHeap, 3)
print(minHeap[0]) # Peek: 3
# Max-heap (workaround)
maxHeap = []
heapq.heappush(maxHeap, -3)
print(-1 * maxHeap[0]) # Peek: 3
Queues (Deque)
from collections import deque
queue = deque()
queue.append(1) # [1]
queue.appendleft(2) # [2, 1]
queue.pop() # [2]
🟪 Python Functions
✅ Defining a Function
Use the def keyword followed by a name and parentheses:
python
Copy
Download
def func():
# code block
You can leave the parameters empty or include them
Code inside the function is indented and only runs when the
function is called
✅ Example:
def func():
print("Run")
func() # Output: Run
You define the function using def
You call it using its name followed by ()
🔁 Nested Functions
Python supports functions inside functions:
def outer():
def inner():
print("Inside inner")
inner()
outer()
Inner function is only accessible from within the outer
function
📦 Functions are Objects
In Python, functions are actually objects, meaning:
o They can be returned
o They can be passed as parameters
o They can be stored in variables
🧮 Function with Arguments
You can define a function with parameters:
def func(x, y):
print(x)
print(y)
func(5, 6)
# Output: 5
# 6
Parameters x and y must be passed when calling the function
🔙 Return Values
Use return to return values from the function:
def func(x, y):
return x * y
print(func(5, 6)) # Output: 30
🧵 Returning Multiple Values
Functions can return multiple values, which are returned as
a tuple:
def func(x, y):
return x * y, x / y
print(func(5, 6)) # Output: (30, 0.83333...)
🧩 Unpacking Return Values
You can unpack multiple return values:
r1, r2 = func(5, 6)
print(r1, r2) # Output: 30 0.83333...
This separates the tuple into individual variables
Optional Parameters (Default Arguments)
You can make parameters optional by giving them a default value:
def func(x, y, z=None):
print(z)
func(5, 6) # Output: None
func(5, 6, 7) # Output: 7
If not passed, the optional parameter (z) defaults to None or
whatever value you specify
✅ Summary of Function Concepts
Concept Example Notes
Basic Definition def func(): Defines a function
With Parameters def func(x, y): Function expects 2 arguments
Calling func(5, 6) Executes the function
Concept Example Notes
Sends value(s) back to the
Returning Values return x * y
caller
Multiple Returns return a, b Returns a tuple
Unpacking Assigns tuple values to
r1, r2 = func()
Return variables
Default
def func(x, y, z=None): z is optional
Parameters
Nested Functions def outer(): def inner(): Inner defined inside outer
Function as Can be passed, returned, stored like Python treats functions as
Object data objects
🟪 *args and **kwargs in Python
These are advanced function parameters that allow your
function to accept a variable number of arguments, both
positional and keyword-based.
🔸 Why Are They Useful?
Sometimes you don't know how many arguments you will get when
calling a function - so Python allows flexibility with:
*args → collects extra positional arguments (as a tuple)
**kwargs → collects extra keyword arguments (as a dict)
✅ *args - Positional Arguments
*args lets you pass any number of non-keyword
arguments to a function
Internally, it collects them into a tuple
def func(*args):
print(args)
func(1, 2, 3, 4) # Output: (1, 2, 3, 4)
You can loop through them:
def func(*args):
for arg in args:
print(arg)
✅ **kwargs - Keyword Arguments
**kwargs collects named arguments into a dictionary
💡 Syntax:
def func(**kwargs):
print(kwargs)
func(a=1, b=2) # Output: {'a': 1, 'b': 2}
You can loop through key-value pairs:
def func(**kwargs):
for key, value in kwargs.items():
print(f"{key} = {value}")
🔁 Combining *args and **kwargs
You can use both in a function, but *args must come
before **kwargs:
def func(*args, **kwargs):
print("Args:", args)
print("Kwargs:", kwargs)
func(1, 2, three=3, four=4)
# Output:
# Args: (1, 2)
# Kwargs: {'three': 3, 'four': 4}
✅ Unpacking with * and ** (outside function)
Python allows you to unpack collections using:
* → list/tuple unpacking
** → dictionary unpacking
🔹 Example with Lists:
def add(x, y):
print(x + y)
pair = [5, 10]
add(*pair) # Output: 15
🔹 Example with Dict:
def greet(name, age):
print(f"{name} is {age} years old")
info = {'name': 'Smriti', 'age': 21}
greet(**info) # Output: Smriti is 21 years old
Order doesn't matter with **kwargs if keys match parameter names.
✅ Advanced Example: Returning Functions
Functions are objects in Python. You can return one function
from another:
def outer(x):
def inner():
print(x)
return inner
f = outer(3)
f() # Output: 3
This is called closure
It shows how Python functions can be passed around like data
⚠️Gotcha: Using *args/**kwargs with print
Unpacking a list or dict directly into print():
args = [1, 2, 3]
kwargs = {'sep': '-', 'end': '\n'}
print(*args, **kwargs) # Output: 1-2-3
But if keys don't match expected keyword arguments, you'll get an
error:
kwargs = {'one': 1}
print(**kwargs) # ❌ Error: 'one' is not a valid keyword arg for print
🔒 Summary Table
Type
Feature Syntax Use Case
Returned
*args *args tuple Any number of positional args
**kwargs **kwargs dict Any number of keyword args
expanded When passing arguments from
Unpacking *list, **dict
values collections
Functions as function Return/Store/Call functions
return inner
Data object dynamically
🟪 Python Scope & global Keyword
🔸 What is Scope?
Scope defines where a variable can be accessed or modified in
your code.
Python uses LEGB Rule to resolve variable names:
Scop
Description
e
L Local - Inside the current function
E Enclosing - In enclosing functions (nested)
G Global - Defined at top-level of script/module
B Built-in - Reserved names in Python (like len, print)
✅ Example: Local vs Global Scope
x = "global value"
def func():
x = "local value"
print(x)
func() # Output: local value
print(x) # Output: global value
Inside func(), x is local and shadows the global x
Changes made inside do not affect the outer x
🔸 Can a Function Modify a Global Variable?
Not unless you explicitly use the global keyword:
🚫 Without global:
x = "Tim"
def func():
x = "Changed"
func()
print(x) # Output: Tim ✅ (not changed)
✅ With global:
x = "Tim"
def func():
global x
x = "Changed"
func()
print(x) # Output: Changed ✅
🧠 Why?
global x tells Python: "Don't make a new local variable - use the
one outside."
⚠️Caution: Avoid global
It breaks encapsulation
Makes debugging harder
Better alternatives:
o Use return values
o Use mutable objects (like lists/dictionaries) if needed
✅ Example with Returning Values (Best Practice)
x = "Tim"
def func(name):
return name
x = func("Changed")
print(x) # Output: Changed ✅
🔄 Extra: Accessing Global from Inside
Even without modifying, you can read a global variable from inside
a function:
x = 10
def func():
print(x)
func() # Output: 10 ✅
But trying to assign to it without global will make a new
local variable, which can confuse your program:
x = 10
def func():
x = x + 1 # ❌ Error: local variable 'x' referenced before assignment
func()
🧠 Summary Table
Keyword Use Case Scope Affected
None Local variable (default) Local to function
Keyword Use Case Scope Affected
global Modify a global variable Global scope
Best
Use return instead Clean code practice
Practice
🟥 Python Exceptions & Exception Handling
🔸 What is an Exception?
An exception is an error that occurs during the execution of a
program, which interrupts the normal flow unless it is handled.
In Python, exceptions can be:
Raised using the raise keyword
Handled using try-except-finally blocks
🚫 Raising Exceptions Manually
✅ Syntax:
raise Exception("Something went wrong")
raise is similar to throw in Java/C++
You can raise built-in exceptions like FileExistsError, ValueError,
etc.
You can also create custom exceptions when you get
into Object-Oriented Programming (OOP) by
subclassing Exception
▶️Example:
raise Exception("Bad") # This halts the program with Exception: Bad
Once Python hits the raise statement, the
program immediately stops execution and throws the error
✅ Raising Specific Built-in Exceptions
raise FileExistsError("File already exists")
raise ValueError("Invalid input")
You can pass a message or description inside the parentheses
🔸 Handling Exceptions with try-except
In Python, the syntax for exception handling is:
try:
# Code that might raise an exception
except ExceptionType as e:
# Handle the exception, 'e' contains the error object
finally:
# Optional cleanup code that runs no matter what
▶️Basic Example:
try:
result = 7 / 0
except Exception as e:
print(e) # Output: division by zero
The try block runs the risky code
If an exception occurs, it's caught in the except block
e stores the actual error object
This prevents the program from crashing
🔹 General except (Catches Any Error):
try:
risky_operation()
except:
print("Something went wrong")
No error type is specified → any exception is caught
This is not recommended in real-world code as it hides the
actual error type
🔹 Specific Exception Catching
You can catch only specific errors:
try:
result = 7 / 0
except ZeroDivisionError as e:
print("Caught division error:", e)
Useful when you want to handle different errors differently
🔸 finally Block
The finally block always executes - whether or not an
exception occurred
Common use: cleanup tasks like closing files, database
connections, etc.
▶️Example:
python
Copy
Download
try:
result = 7 / 0
except Exception as e:
print("Caught:", e)
finally:
print("This will always run")
🧾 Output:
Caught: division by zero
This will always run
Summary Table:
Componen
Purpose Notes
t
raise Manually trigger an exception Like throw in other languages
try Wrap code that may fail Starts the protected block
except Catch the exception Can be general or specific
as e Access exception object e holds the error message
finally Run regardless of exception Good for cleanup
🔁 Example Recap
try:
x=7/0
except ZeroDivisionError as e:
print("Handled:", e)
finally:
print("Always runs")
🧾 Output:
Handled: division by zero
Always runs
🟦 F-Strings (Formatted Strings)
✅ What is an F-string?
Introduced in Python 3.6+
Allows for embedding expressions and variables directly
inside strings
Very readable and efficient way to format strings
✅ Syntax:
f"some string {expression_or_variable}"
Can use either lowercase f or uppercase F before the string
Anything inside {} will be evaluated and turned into a string
▶️Examples:
1. Evaluating an Expression:
s = f"6 + 8 = {6 + 8}"
print(s) # Output: 6 + 8 = 14
2. Embedding Variables:
name = "Smriti"
score = 89
message = f"Hello {name}, your score is {score}"
print(message) # Output: Hello Smriti, your score is 89
🛠 Why use F-strings?
Cleaner alternative to:
"Hello " + name + ", your score is " + str(score)
Avoids manual str() conversion and concatenation
More readable and maintainable
Faster execution than string concatenation
🔹 Advanced F-String Features
Formatting Numbers:
price = 49.99
print(f"Price: {price:.2f}") # Output: Price: 49.99
Calling Functions:
def get_name():
return "Alice"
print(f"Name: {get_name()}") # Output: Name: Alice
Multi-line F-strings:
name = "Bob"
age = 25
message = (
f"Name: {name}\n"
f"Age: {age}\n"
f"Next year: {age + 1}"
)
print(message)
Using Expressions:
items = 3
price = 9.99
print(f"Total: ${items * price:.2f}") # Output: Total: $29.97
⚠️Important Notes:
1. F-strings are evaluated at runtime
2. The expressions inside {} must be valid Python expressions
3. You can't use backslashes (\) directly inside the curly braces
4. F-strings are faster than % formatting and str.format()
🔹 Comparison with Other String Formatting Methods
1. % formatting (old style):
"Hello %s, your score is %d" % (name, score)
2. str.format() (new style):
"Hello {}, your score is {}".format(name, score)
3. f-strings (best for Python 3.6+):
f"Hello {name}, your score is {score}"
F-strings are generally preferred because:
More readable
Less verbose
Faster execution
Variables are visible in the same line where they're used
🧠 Summary Table:
Feature Example Notes
Basic f-string f"Hello {name}" Simple variable insertion
Can include any valid
Expressions f"Total: {price * quantity}"
expression
Formatting
f"Price: {price:.2f}" Supports all format specifiers
numbers
Function calls f"Name: {get_name()}" Can call functions inside
Multi-line f"Line1\nLine2" Works with multi-line strings
Compiled at runtime for
Performance Faster than other methods
efficiency
🟩 Lambda Functions in Python
✅ What is a Lambda?
A lambda is a one-line anonymous function
Unlike regular functions, it does not use the def keyword
It's primarily used when you need a small function
temporarily, especially in higher-order functions
like map, filter, and sorted
✅ Syntax
lambda arguments: expression
▶️Examples:
1. Basic Lambda
x = lambda x: x + 5
print(x(2)) # Output: 7
2. Multiple Parameters
add = lambda x, y: x + y
print(add(2, 32)) # Output: 34
❗ Important Note:
Although assigning a lambda to a variable is possible, it's not
the recommended way to use lambdas
They're better used directly inside functions like map() or filter()
If your function is complex enough to need a name, you should
use a regular def function instead
🔹 Why Use Lambda Functions?
1. Conciseness: Write simple functions in a single line
2. Anonymous: Don't need to name temporary functions
3. Functional Programming: Work seamlessly with map(), filter(),
and sorted()
4. Readability: When used properly, can make code more
readable for simple operations
🔹 Limitations of Lambda Functions
1. Can only contain a single expression
2. Cannot include statements like if, for, while, etc. (though
conditional expressions are allowed)
3. No annotations or type hints
4. Less readable for complex operations
▶️More Examples:
Conditional Expression
is_even = lambda x: True if x % 2 == 0 else False
print(is_even(4)) # Output: True
Sorting with Lambda
names = ['Alice', 'Bob', 'Charlie', 'Dave']
sorted_names = sorted(names, key=lambda x: len(x))
print(sorted_names) # Output: ['Bob', 'Dave', 'Alice', 'Charlie']
🟨 map() and filter() Functions
These are built-in higher-order functions in Python and are often
used in combination with lambdas.
✅ map(function, iterable)
Purpose:
Applies a function to each element in the iterable
Returns a map object (convert it using list() to see the output)
▶️Example:
x = [1, 2, 4, 5, 6, 7]
mapped = map(lambda i: i + 2, x)
print(list(mapped)) # Output: [3, 4, 6, 7, 8, 9]
🔁 More Variation:
mapped = map(lambda i: i * 2, x)
print(list(mapped)) # Output: [2, 4, 8, 10, 12, 14]
Using Regular Function with map():
def add_two(n):
return n + 2
numbers = [1, 2, 3]
result = map(add_two, numbers)
print(list(result)) # Output: [3, 4, 5]
✅ filter(function, iterable)
Purpose:
Filters elements from iterable for which the function returns
True
Returns a filter object
▶️Example using Lambda:
x = [1, 2, 4, 5, 6, 7]
filtered = filter(lambda i: i % 2 == 0, x)
print(list(filtered)) # Output: [2, 4, 6]
▶️Example using Regular Function:
def is_even(i):
return i % 2 == 0
filtered = filter(is_even, x)
print(list(filtered)) # Output: [2, 4, 6]
✅ This shows why lambda is handy - we don't need to define a
separate function if it's small or used only once.
🔄 Comparing map() and filter()
Function Purpose Return Value Example
Transforms each map object map(lambda x: x*2,
map()
element (iterator) [1,2,3]) → [2,4,6]
Filters elements by filter object filter(lambda x: x>2,
filter()
condition (iterator) [1,2,3]) → [3]
🔹 Practical Examples
1. Processing Strings
names = ['alice', 'bob', 'charlie']
capitalized = map(lambda x: x.capitalize(), names)
print(list(capitalized)) # Output: ['Alice', 'Bob', 'Charlie']
2. Filtering Negative Numbers
numbers = [5, -2, 10, -8, 3]
positive = filter(lambda x: x > 0, numbers)
print(list(positive)) # Output: [5, 10, 3]
3. Combining map and filter
numbers = [1, 2, 3, 4, 5]
result = map(lambda x: x**2, filter(lambda x: x % 2 == 0, numbers))
print(list(result)) # Output: [4, 16] (squares of even numbers)
🔹 Performance Considerations
1. Lazy Evaluation: Both map and filter return iterators, meaning
they don't compute values until needed
2. Memory Efficient: Process one item at a time rather than
creating intermediate lists
3. Often Faster than equivalent list comprehensions for large
datasets
🔹 Alternative: List Comprehensions
While map and filter are useful, Python also offers list
comprehensions that can often achieve the same result more
readably:
map() equivalent
# Using map
result = map(lambda x: x*2, numbers)
# Using list comprehension
result = [x*2 for x in numbers]
filter() equivalent
# Using filter
result = filter(lambda x: x > 0, numbers)
# Using list comprehension
result = [x for x in numbers if x > 0]
🧠 When to Use Which?
Use map/filter with lambdas for simple operations
Use list comprehensions for more complex transformations or
filtering
Consider readability - sometimes list comprehensions are
clearer
For very large datasets, map/filter might be more memory
efficient
🔚 Summary Table
Concept Syntax Example Best For
Lambda
lambda x: x + 1 Small, one-time use functions
Function
Applying same operation to
map() map(func, iterable)
all items
Selecting items that meet
filter() filter(func, iterable)
condition
Combined map(func1, filter(func2, iterable)) Processing filtered items