Phase 01

Prerequisites & Setup

Learn to think in Python: values, decisions, loops and functions.

Phase 1: Prerequisites & Setup

Welcome. This phase assumes you have never written a line of code. By the end of it you will be able to read a program and predict what it does, and you will have written sixteen small Python programs of your own: programs that read input, do arithmetic, make decisions, repeat work in loops, and pull apart text and lists. Everything later on this site (data structures, searching, graphs) is built from exactly these pieces, so it is worth going slowly here and making sure each one feels comfortable.

Read a section, run its examples, then do the practice problem before moving on. Typing the examples yourself, rather than only reading them, is what makes them stick.

How this site works

  • Your code runs entirely in your browser. Nothing is installed on your computer and nothing is sent to a server. The first load of the Python engine may take a few seconds.
  • Run executes your code and shows whatever it prints, plus any error message. Use it freely to experiment.
  • Submit runs your code against the problem's tests and tells you which passed. Problems in the first part of this phase are scripts: the tests feed input to your program and compare what it prints. Later problems ask you to write a function; the tests call it with different arguments and compare what it returns.
  • Some tests are hidden. You can see their names but not their inputs. They usually check an edge case the description mentions (an empty list, a negative number, zero). If a hidden test fails, re-read the constraints.
  • Progress is saved on this device, in your browser. Clearing site data or switching browsers will lose it.
  • Hints are there to be used. They are written so that the first one nudges you and the last one nearly spells out the approach.

Printing text

A Python program is a list of instructions read from top to bottom. The simplest instruction is print(), which writes something to the screen.

print("Hello, World!")
print("I am learning Python.")

Output:

Hello, World!
I am learning Python.

Text inside quotes is called a string. You can use double quotes "..." or single quotes '...', as long as you close with the same kind you opened with. Each print() writes its text and then moves to the next line.

Common mistakes:

  • Forgetting the quotes: print(Hello) makes Python look for a variable named Hello, and fails with NameError.
  • Forgetting a bracket: print("Hi" gives a SyntaxError. Read the error message; it usually points at the line.
  • Mismatched output. The checker compares every character, so Hello world is not the same as Hello, World!.

Practice: Hello, World!

Reading input and storing it in a variable

input() pauses the program, reads one line typed by the user (or, on this site, the next line of the test's input), and gives it back as a string. To keep that value you put it in a variable with =:

name = input()
print("Hello, " + name + "!")

If the input is Ada, the output is:

Hello, Ada!

A variable is a name that refers to a value. name = input() means "run input(), then remember the result under the name name". The + between strings glues them together (this is called concatenation).

Common mistakes:

  • input() always returns a string, even if the user types 42. input() + 1 fails because you cannot add a string and a number.
  • Variable names are case-sensitive: Name and name are different variables.

Practice: Personal Greeting

Numbers and arithmetic

To do maths with input, convert it to an integer with int():

a = int(input())   # input "7" becomes the number 7
b = int(input())   # input "2" becomes the number 2
print(a + b)       # 9
print(a - b)       # 5
print(a * b)       # 14
print(a / b)       # 3.5   (true division, always gives a float)
print(a // b)      # 3     (floor division: round DOWN to a whole number)
print(a % b)       # 1     (remainder: 7 = 2 * 3 + 1)
print(a ** b)      # 49    (power: 7 squared)

Anything after # on a line is a comment. Python ignores it; it is there for humans.

Python has two kinds of numbers you will use constantly: int (whole numbers like 7, -3, 0) and float (numbers with a decimal point like 3.5). / always produces a float, even when the division is exact: 6 / 3 is 2.0. If you want a whole-number answer, use //.

Floor division rounds down, which matters for negatives: -7 // 2 is -4, not -3. The remainder then works out so that a == b * (a // b) + a % b is always true, which is why -7 % 2 is 1.

The usual order of operations applies: ** first, then * / // %, then + -. Use brackets when in doubt: 2 * (3 + 4) is 14, while 2 * 3 + 4 is 10.

Practice: Six-Line Calculator

Variables and f-strings

Good programs name their intermediate results. Compare:

print(2 * (int(input()) + int(input())))

with:

width = int(input())
height = int(input())
perimeter = 2 * (width + height)
print(perimeter)

Both work, but only the second can be read by a human at a glance. Use descriptive names made of lower-case words joined with underscores: total_price, num_students.

To mix values into text, use an f-string: put f before the opening quote and write expressions inside curly braces.

width = 3
height = 4
print(f"Area: {width * height}")
print(f"The rectangle is {width} by {height}.")

Output:

Area: 12
The rectangle is 3 by 4.

Common mistakes:

  • Forgetting the f. print("Area: {area}") prints the braces literally.
  • Mixing types with +: "Area: " + 12 fails. Either use an f-string or convert with str(12).

Practice: Rectangle Report

Types, conversion and formatting floats

Every value has a type. You can ask with type():

print(type(42))       # <class 'int'>
print(type(3.5))      # <class 'float'>
print(type("42"))     # <class 'str'>

Converting between them is done with functions named after the type: int("42") is 42, float("2.5") is 2.5, str(7) is "7". Converting text that is not a number, such as int("hello"), raises a ValueError.

Floats are often printed with more digits than you want:

avg = (1 + 2 + 4) / 3
print(avg)             # 2.3333333333333335
print(f"{avg:.2f}")    # 2.33
print(round(avg, 1))   # 2.3

The :.2f inside the braces is a format specification: "show this as a fixed-point number with 2 digits after the decimal point". It pads with zeros if needed, so f"{5 / 1:.2f}" gives 5.00. This is the tool to reach for whenever a problem says "rounded to two decimal places".

Practice: Average of Three

Functions

From here on, problems ask you to write a function rather than a whole script. A function is a named block of code that takes inputs, called parameters, and hands back an answer with return.

def double(n):
    return n * 2

print(double(4))     # 8
print(double(21))    # 42

Read def double(n): as "define a function called double that takes one parameter n". The body is indented by four spaces; indentation is how Python knows which lines belong to the function. return ends the function and sends a value back to the caller. The tests on this site call your function and look at the returned value, so:

  • print inside a function shows text on the screen but does not count as returning it. A function that only prints returns None, and the test will fail.
  • A function with no return at all also returns None.

You can define a function once and call it as many times as you like with different arguments. That is the whole point.

Making decisions with if / elif / else

Programs need to choose between paths. The if statement runs its indented block only when its condition is true:

def sign(n):
    if n > 0:
        return "positive"
    elif n < 0:
        return "negative"
    else:
        return "zero"

print(sign(5))    # positive
print(sign(-2))   # negative
print(sign(0))    # zero

Python checks the conditions from the top. The first true one wins, its block runs, and the rest are skipped. elif means "else, if", and else catches everything that did not match. You can have as many elif branches as you like, and elif/else are optional.

The comparison operators are <, >, <=, >=, == (equal) and != (not equal). Note the double == for comparison; a single = is assignment.

Common mistakes:

  • if n = 0: is a syntax error. You meant ==.
  • Forgetting the colon at the end of if, elif and else lines.
  • Inconsistent indentation. Pick four spaces and stick to it.
  • Ordering: if you write if score >= 60 before if score >= 90, a score of 95 will match the first one. Put the most specific condition first.

Practice: Sign of a Number

Booleans and logic

A comparison produces a boolean: either True or False. Booleans are values like any other; you can store them and return them.

year = 2024
print(year % 4 == 0)          # True
is_even = year % 2 == 0
print(is_even)                # True

Combine conditions with and, or and not:

age = 20
has_ticket = False
print(age >= 18 and has_ticket)      # False  (both must be true)
print(age >= 18 or has_ticket)       # True   (at least one is true)
print(not has_ticket)                # True

Use brackets to group when mixing and and or, because and binds tighter: a or b and c means a or (b and c).

The % operator is the standard way to test divisibility: n % k == 0 is True exactly when n is a multiple of k. Similarly n % 2 == 0 tests for even numbers.

Common mistake: writing if is_even == True:. It works, but if is_even: says the same thing. And never return the strings "True" or "False"; the tests want real booleans.

Practice: Leap Year

Repeating with while

A while loop repeats its block as long as a condition stays true:

n = 6
steps = 0
while n != 1:
    if n % 2 == 0:
        n = n // 2
    else:
        n = 3 * n + 1
    steps += 1
print(steps)     # 8

steps += 1 is shorthand for steps = steps + 1. The same works for -=, *= and //=.

Every while loop has three parts: something set up before the loop (steps = 0), a condition that is checked before each trip (n != 1), and something inside the loop that moves towards making the condition false (changing n). Forget the third part and the loop runs forever. If Run seems to hang, that is almost always why; reload the page and check the loop body.

while is the right choice when you do not know in advance how many repetitions you need. When you do know, for is nicer.

Practice: Collatz Steps

Repeating with for and range

for walks through a sequence, giving the loop variable each value in turn. range() produces a sequence of integers:

for i in range(5):
    print(i)

Output:

0
1
2
3
4

range(5) gives 0 up to but not including 5. range(1, 6) gives 1, 2, 3, 4, 5. range(0, 10, 2) gives 0, 2, 4, 6, 8 (the third number is the step). The "stop before the end" rule surprises everyone at first; when you want the numbers 1 to n inclusive, write range(1, n + 1).

A very common pattern is the accumulator: a variable that starts at a neutral value and is updated on each trip.

total = 0
for i in range(1, 11):
    total += i
print(total)        # 55

product = 1
for i in range(1, 6):
    product *= i
print(product)      # 120

For a sum the neutral starting value is 0; for a product it is 1.

Practice: Factorial

Functions with several parameters and several results

A function can take as many parameters as you like, separated by commas, and the arguments are matched up by position:

def rectangle_area(width, height):
    return width * height

print(rectangle_area(3, 4))    # 12

It can also return more than one value. Separate them with commas after return; Python packs them into a tuple, which is a list-like value that cannot be changed:

def divide(a, b):
    return a // b, a % b

result = divide(17, 5)
print(result)          # (3, 2)

q, r = divide(17, 5)   # unpack into two variables
print(q, r)            # 3 2

The second form, q, r = ..., is called unpacking and is very common in Python.

Practice: Smallest and Largest

Strings: indexing, slicing and methods

A string is a sequence of characters, and you can look inside it. Positions are counted from 0:

s = "python"
print(len(s))     # 6
print(s[0])       # p
print(s[5])       # n
print(s[-1])      # n   (negative index counts from the end)

A slice s[start:stop] takes the characters from start up to but not including stop. Leave either side blank to mean "from the beginning" or "to the end". A third number is a step, and a step of -1 walks backwards:

print(s[0:3])     # pyt
print(s[2:])      # thon
print(s[:-1])     # pytho
print(s[::-1])    # nohtyp   (reversed)

Strings come with methods, functions you call with a dot:

print(s.upper())          # PYTHON
print("Hello".lower())    # hello
print("a" in s)           # False
print("th" in s)          # True

Strings cannot be changed in place; every method returns a new string. s.upper() on its own does nothing useful unless you keep the result: s = s.upper().

Practice: Is It a Palindrome?

More string methods: split, join, strip, count, replace

These five methods handle most everyday text processing.

sentence = "  the quick  brown fox "
words = sentence.split()
print(words)                        # ['the', 'quick', 'brown', 'fox']
print(sentence.strip())             # 'the quick  brown fox'
print("-".join(words))              # the-quick-brown-fox
print(sentence.count("o"))          # 2
print(sentence.replace("fox", "cat"))   # '  the quick  brown cat '
  • split() with no argument splits on any run of whitespace and throws away leading and trailing spaces. split(",") splits on commas instead.
  • join is called on the separator: ", ".join(["a", "b"]) gives "a, b". It only accepts a list of strings.
  • strip() removes whitespace from both ends. lstrip() and rstrip() do one end only.
  • count(sub) counts non-overlapping occurrences of sub.
  • replace(old, new) swaps every occurrence.

You can loop directly over the characters of a string with for ch in s:, and over its words with for word in s.split():.

Practice: Initials

Lists

A list is an ordered collection of values in square brackets. Unlike strings, lists can be changed.

nums = [3, 7, 2]
print(len(nums))     # 3
print(nums[0])       # 3
print(nums[-1])      # 2
nums.append(10)      # add to the end
print(nums)          # [3, 7, 2, 10]
nums[1] = 8          # replace an element
print(nums)          # [3, 8, 2, 10]
print(sum(nums))     # 23
print(7 in nums)     # False

Indexing and slicing work exactly as they do for strings. The two most common ways to loop over a list are by value and by position:

for x in nums:                 # by value
    print(x)

for i in range(len(nums)):     # by position
    print(i, nums[i])

Use the second form when you need to know where you are, for example to compare nums[i] with nums[i + 1].

Python has max(), min() and sum() built in, and you will use them constantly. But it is important to understand the loop they hide, because the same "best so far" pattern shows up in problems that the built-ins cannot solve:

best = nums[0]
for x in nums:
    if x > best:
        best = x

Start from the first element, not from 0, or a list of negative numbers will give the wrong answer.

Common mistakes:

  • nums[len(nums)] is one past the end and raises IndexError. The last valid index is len(nums) - 1, or just use nums[-1].
  • nums.append(x) returns None; write it on its own line, not nums = nums.append(x).

Practice: Largest in a List

Nested loops

A loop inside a loop. The inner loop runs to completion for every single trip of the outer loop.

for i in range(1, 4):
    for j in range(1, 4):
        print(i * j, end=" ")
    print()

Output:

1 2 3
2 4 6
3 6 9

(end=" " tells print to put a space instead of a newline after the value; a bare print() ends the line.)

The classic use is comparing every pair of elements in a list. To visit each unordered pair once, start the inner loop just after the outer index:

for i in range(len(nums)):
    for j in range(i + 1, len(nums)):
        ...   # nums[i] and nums[j], with i < j

If the inner loop started at 0 you would see each pair twice (once as (i, j), once as (j, i)) and also compare each element with itself.

Nested loops are slow for big inputs (a list of 1000 items means about 500,000 pair checks), which is a theme later phases return to. For the small inputs here they are fine.

Practice: Count the Pairs

Putting it together: FizzBuzz

FizzBuzz is the most famous beginner exercise, and it combines a for loop, %, an if/elif/else chain, str() and append. Rather than printing, our version returns a list, which is how most later problems on this site work.

Two details trip people up. First, the order of the branches: the "divisible by both" check must come before the single ones, otherwise 15 matches "divisible by 3" and never reaches "FizzBuzz". Second, the plain numbers must be converted with str(i) so that every element of the list has the same type.

Practice: FizzBuzz List

Capstone

The last problem asks for a list of vowel counts, one per word of a sentence. Nothing new is required; it is the combination that makes it a good test. Sketch the shape before you type:

  1. Split the sentence into words.
  2. For each word, count its vowels with an inner loop and a condition.
  3. Append that count to a result list.
  4. Return the list.

If you get stuck, solve the inner step first as its own function (count_vowels(word)), test it with Run, then wrap the outer loop around it. Breaking a problem into smaller functions is a skill you will use for the rest of the course.

Practice: Vowels per Word

Checklist

Before moving on to Phase 2, make sure you can do each of these without looking anything up:

  • Print text and numbers, and explain why print(Hello) fails.
  • Read a line with input() and turn it into a number with int().
  • Predict the results of /, //, % and **, including -7 // 2.
  • Build a message with an f-string and format a float to two decimal places.
  • Define a function with def, and explain the difference between print and return.
  • Write an if / elif / else chain and order the branches correctly.
  • Combine comparisons with and, or, not, and test divisibility with %.
  • Write a while loop with a counter, and say what would make it run forever.
  • Use range(1, n + 1) to loop from 1 to n inclusive, and build a sum or product in an accumulator.
  • Return two values from a function and unpack them.
  • Index and slice a string, reverse it with [::-1], and lower-case it.
  • Use split, join, strip, count and replace.
  • Create a list, append to it, index it, and loop over it by value and by position.
  • Find the largest element with a loop and explain why you start from nums[0].
  • Write a nested loop that visits each pair of positions exactly once.
  • Solve FizzBuzz and the vowel-count capstone from a blank editor.