Beginner
12 min read
#Python#Basics#Data Types#Control Flow#Operators

Python Basics — The Complete Notebook

Comprehensive guide on Python Basics — The Complete Notebook.

Python Basics

1. Overview#

Python is a dynamically typed, interpreted, high-level language built around readability. This note covers the true foundation: how Python stores values, how it makes decisions, and how it repeats work. Everything else in the language — OOP, decorators, async — is built on top of these core mechanics.

Everything in Python is an object, including integers, functions, and modules. Each object carries a reference count, a type descriptor, and a value in memory.


2. The Type System#

2.1 Built-in Data Types#

TypeExampleMutable?
int42No
float3.14No
str"hello"No
boolTrue, FalseNo
NoneTypeNoneN/A
list[1, 2, 3]Yes
tuple(1, 2, 3)No
dict{"a": 1}Yes
set{1, 2, 3}Yes

2.2 Dynamic Typing#

Python doesn't require declaring a variable's type — the type is attached to the value, not the variable name.

🐍 Python
x = 10 # x refers to an int x = "ten" # now x refers to a str — completely legal print(type(x)) # <class 'str'>

2.3 Type Checking#

🐍 Python
value = 42 print(type(value) == int) # Works, but fragile with subclasses print(isinstance(value, int)) # Preferred — respects inheritance

2.4 Type Conversion (Casting)#

🐍 Python
age_str = "25" age_int = int(age_str) # "25" -> 25 price = float("19.99") # "19.99" -> 19.99 flag = bool(0) # 0 -> False (falsy) items = list("abc") # "abc" -> ['a', 'b', 'c']

Falsy values in Python: 0, 0.0, "", [], {}, (), set(), None, False. Everything else is truthy.


3. Memory Model: Mutability vs Immutability#

CategoryData TypesMemory Behavior
Immutableint, float, str, tuple, frozenset, bytesValues cannot be modified in-place; changes allocate a new object.
Mutablelist, dict, set, bytearrayValues can be modified in-place without changing memory address (id).
🐍 Python
# Immutable strings text = "hello" print(id(text)) text += " world" print(id(text)) # New memory location! # Mutable lists data_points = [10, 20, 30] print(id(data_points)) data_points.append(40) print(id(data_points)) # Same memory location

4. Operators#

4.1 Arithmetic & Comparison#

🐍 Python
a, b = 17, 5 print(a // b) # 3 — floor division print(a % b) # 2 — modulo print(a ** b) # 1419857 — exponentiation print(a != b) # True

4.2 Logical Operators#

🐍 Python
age = 25 has_id = True print(age >= 18 and has_id) # True print(age < 18 or has_id) # True print(not has_id) # False

4.3 Identity vs Equality#

🐍 Python
a = [1, 2, 3] b = [1, 2, 3] c = a print(a == c) # True — same value print(a is c) # True — same object print(a == b) # True — same value print(a is b) # False — different objects in memory

4.4 The Walrus Operator (:=)#

Introduced in Python 3.8 — assigns and returns a value in the same expression, reducing repeated computation.

🐍 Python
data = [1, 2, 3, 4, 5, 6, 7, 8] # Without walrus n = len(data) if n > 5: print(f"List is long: {n} items") # With walrus if (n := len(data)) > 5: print(f"List is long: {n} items")

5. Control Flow#

5.1 Conditionals#

🐍 Python
score = 82 if score >= 90: grade = "A" elif score >= 75: grade = "B" elif score >= 60: grade = "C" else: grade = "F" print(grade) # B

5.2 for Loops & range#

🐍 Python
for i in range(0, 10, 2): # start, stop, step print(i) # 0, 2, 4, 6, 8 for index, name in enumerate(["Asha", "Ravi", "Meera"]): print(index, name)

5.3 while Loops#

🐍 Python
attempts = 0 while attempts < 3: print(f"Attempt {attempts + 1}") attempts += 1

5.4 break, continue, and the Loop else#

The else block on a loop runs only if the loop completes without hitting break — useful for search patterns.

🐍 Python
numbers = [4, 8, 15, 16, 23, 42] target = 99 for n in numbers: if n == target: print("Found it!") break else: print("Target not found") # This runs, since break never triggered

6. Core Data Structures#

6.1 Lists — Ordered, Mutable#

🐍 Python
fruits = ["apple", "banana", "cherry"] fruits.append("date") fruits.insert(1, "avocado") fruits.remove("banana") print(fruits[-1]) # date — negative indexing print(fruits[1:3]) # slicing

6.2 Tuples — Ordered, Immutable#

🐍 Python
coordinates = (12.9716, 77.5946) # lat, long lat, long = coordinates # unpacking print(f"Lat: {lat}, Long: {long}")

6.3 Dictionaries — Key-Value Pairs#

🐍 Python
user = {"name": "Kamal", "role": "Engineer"} user["company"] = "Hyperthink Systems" # add new key print(user.get("email", "Not provided")) # safe access with default for key, value in user.items(): print(f"{key}: {value}")

6.4 Sets — Unique, Unordered#

🐍 Python
tags_a = {"python", "ai", "backend"} tags_b = {"python", "ml", "frontend"} print(tags_a & tags_b) # intersection: {'python'} print(tags_a | tags_b) # union print(tags_a - tags_b) # difference: {'ai', 'backend'}

7. String Formatting#

🐍 Python
name = "Kamal" score = 95.5 # f-strings (preferred, Python 3.6+) print(f"{name} scored {score:.1f}%") # .format() method print("{} scored {:.1f}%".format(name, score)) # % formatting (legacy, still seen in older code) print("%s scored %.1f%%" % (name, score))

7.1 Useful f-string Tricks#

🐍 Python
value = 3.14159265 print(f"{value:.2f}") # 3.14 — 2 decimal places print(f"{1000000:,}") # 1,000,000 — thousands separator print(f"{'text':>10}") # right-align within 10 chars print(f"{value=}") # value=3.14159265 — debug-friendly, Python 3.8+

8. Idiomatic Python Patterns#

8.1 List & Dict Comprehensions#

Comprehensions run at C-level speed inside the interpreter, making them faster and more readable than manual loops.

🐍 Python
raw_scores = [45, 88, 92, 31, 78, 99, 100] high_scores = [score for score in raw_scores if score >= 80] feature_names = ["age", "income", "credit_score"] feature_idx_map = {name: idx for idx, name in enumerate(feature_names)}

8.2 Context Managers (with statement)#

Ensures deterministic resource cleanup (files, DB connections, locks).

🐍 Python
class ModelArtifactManager: def __init__(self, filepath): self.filepath = filepath def __enter__(self): self.file = open(self.filepath, "w") return self.file def __exit__(self, exc_type, exc_val, exc_tb): self.file.close() return False

9. Common Pitfalls#

INCORRECT: Mutable Default Arguments#

🐍 Python
def append_prediction(val, container=[]): container.append(val) return container print(append_prediction(1)) # [1] print(append_prediction(2)) # [1, 2] -> shared across calls!

CORRECT: Fix#

🐍 Python
def append_prediction(val, container=None): if container is None: container = [] container.append(val) return container

INCORRECT: Modifying a List While Iterating#

🐍 Python
nums = [1, 2, 3, 4, 5] for n in nums: if n % 2 == 0: nums.remove(n) # skips elements — unpredictable results

CORRECT: Fix#

🐍 Python
nums = [1, 2, 3, 4, 5] nums = [n for n in nums if n % 2 != 0]

10. Summary & Best Practices Checklist#

  • Use is for identity/None checks, == for value equality.
  • Prefer f-strings for readability and performance.
  • Never use a mutable object (list, dict) as a default argument.
  • Use enumerate() instead of manual index counters.
  • Use comprehensions for simple transforms; fall back to loops when logic gets complex.
  • Use dict.get() with a default instead of risking a KeyError.
  • Reach for tuples when data shouldn't change (e.g., coordinates, RGB values).
Knowledge Checkpoint

Python Memory Model & Fundamentals Checkpoint

Q1.Which of the following Python data types is MUTABLE in memory?
Astr
Btuple
Clist
Dfrozenset
Q2.What happens when using a mutable default argument such as `def fn(val, container=[])`?
APython raises a SyntaxError at compile time.
BThe default list is instantiated once at function definition time and shared across subsequent calls.
CPython automatically creates a new empty list on each call.
DThe list is converted into a frozenset on the first invocation.
Q3.What is the time complexity of checking membership (`x in collection`) in a standard Python `set` versus a `list` on average?
AO(1) for set, O(n) for list
BO(n) for set, O(1) for list
CO(log n) for set, O(1) for list
DO(1) for set, O(log n) for list
Q4.What is the difference between `==` and `is` in Python?
A`==` checks memory address identity, whereas `is` checks value equality.
B`==` checks value/equality equivalence via `__eq__`, whereas `is` checks object memory identity (`id(a) == id(b)`).
C`is` can only be used with boolean literals.
DThere is no difference; they are aliases for each other.
Track Your Learning

Finished studying this notebook?

Mark this guide as completed to update your course progress roadmap.