Python from scratch — every fundamental explained clearly, building up to solving real problems, with links to the Problem Solving patterns and complexity.
Python is where I'd start learning to program and to solve problems, because the language
gets out of the way: it reads almost like English, handles the fiddly stuff (memory, types) for
me, and comes with batteries included (huge standard library). This note teaches it from a blank
page and then connects each piece back to the thinking in
Problem Solving — the why behind the how.
The golden idea: learn the building blocks, then learn the patterns. Python is the
building blocks; Problem Solving is the patterns. You need both.
A variable is just a name pointing at a value. No type declarations — Python figures out the
type from the value (dynamic typing), and a name can be reassigned to any type.
name = "Asha" # str (text)age = 20 # int (whole number)
name = input("Enter your name: ") # always returns a stringage = int(input("Enter your age: ")) # convert to int for mathsprint("Hello", name) # print can take many args (space-separated)print(f"Next year you'll be {age + 1}") # f-string: drop values inside {}
f-strings are the modern way to build text: put an f before the quotes and write
expressions inside { }, e.g. f"Total = {price * qty}".
print adds a newline by default; control it with print(x, end="") and print(a, b, sep=", ").
# for over a range of numbersfor i in range(5): # 0, 1, 2, 3, 4 print(i)# for over any iterable (list, string, ...)for ch in "cat": print(ch)# whilecount = 0while count < 3: print(count) count += 1
range(start, stop, step) — stop is excluded.
break exits the loop; continue skips to the next iteration.
enumerate gives index + value: for i, x in enumerate(items):.
zip walks two lists together: for a, b in zip(list1, list2):.
The workhorse — an ordered, mutable sequence (Python's dynamic array). Maps onto the array in
Problem Solving.
nums = [3, 1, 4, 1, 5]nums.append(9) # add to endnums[0] = 30 # change in place (mutable)print(nums[1:3]) # slicing -> [1, 4]print(len(nums)) # 6nums.sort() # sort in placeprint(sorted(nums)) # return a new sorted list
Common methods: append, pop, insert, remove, index, count, reverse, sort.
List comprehension — build a list in one readable line:
squares = [x * x for x in range(5)] # [0, 1, 4, 9, 16]evens = [x for x in nums if x % 2 == 0] # filter while building
Key → value pairs — Python's hash map (the same idea as the hash map in
Problem Solving, with average O(1) lookup). Written with curly braces
{key: value}.
ages = {"Asha": 20, "Ravi": 22}print(ages["Asha"]) # 20ages["Meena"] = 19 # add / updateprint("Ravi" in ages) # True (checks keys)for name, age in ages.items(): print(name, age)
An unordered collection of unique items — written {1, 2, 3}. Great for removing duplicates
and fast membership tests (O(1)).
s = {1, 2, 2, 3} # -> {1, 2, 3} (duplicate dropped)s.add(4)print(3 in s) # True, fasta, b = {1, 2, 3}, {2, 3, 4}print(a & b) # intersection -> {2, 3}print(a | b) # union -> {1, 2, 3, 4}
Which to use? Ordered + changeable → list. Fixed group → tuple. Lookup by key /
counting → dict. Unique items / fast "is it in here?" → set. This choice is half of
problem solving.
Read and write files with with open(...), which closes the file automatically.
with open("data.txt", "w") as f: # "w" write, "r" read, "a" append f.write("hello\n")with open("data.txt", "r") as f: text = f.read() # whole file as a string # for line in f: ... # or line by lineprint(text)
Here's where it all comes together. These are the same classic programs from the C notes, but
notice how much shorter and clearer Python is — and each one uses a pattern from
Problem Solving.
Swap two variables — Python does it in one line (tuple unpacking):
a, b = b, a
Odd or even / largest of three:
print("Even" if n % 2 == 0 else "Odd")print(max(a, b, c)) # built-in max beats writing if/else
Factorial & Fibonacci:
fact = 1for i in range(1, n + 1): fact *= i# first n Fibonacci numbersa, b = 0, 1for _ in range(n): print(a, end=" ") a, b = b, a + b # swap-and-add in one line
Prime check:
def is_prime(n): if n < 2: return False for i in range(2, int(n ** 0.5) + 1): # only up to sqrt(n) if n % i == 0: return False return True
Linear search is just in; sorting is just sorted() — Python hands you what C made you
write by hand.
Two Sum — the hash-map pattern from Problem Solving, in real Python:
def two_sum(nums, target): seen = {} # value -> index (a dict = hash map) for i, x in enumerate(nums): if target - x in seen: # have I seen the complement? return [seen[target - x], i] seen[x] = i return []
This is O(n) — see the complexity section for why that beats the
O(n²) nested-loop version.
The Big-O rules from Problem Solving apply directly — and it helps to
know what Python's structures cost:
Operation
list
dict / set
index / key lookup
O(1)
O(1) avg
x in ...
O(n)
O(1) avg
append / add
O(1) amortized
O(1) avg
insert/remove at front
O(n)
—
pop(0)
O(n) (use deque!)
—
Big one: testing x in my_list is O(n), but x in my_set is O(1). Swapping a list for a set
is the most common Python speed-up — exactly the "trade space for time" move from
Problem Solving.