Why this matters
A mirror image is what you see when a picture is reversed from left to right. A shape is symmetric when its left side matches its right side. A pyramid, a diamond and the capital letter A all look the same on both sides. Pattern questions often use this kind of shape, and symmetry lets you write less code.
If you know the left half of a picture, you can build the right half from it. When one rule makes both halves, the halves cannot disagree. This removes a common mistake, where the left side is correct and the right side is one column out of place.
Earlier lessons in this module built pyramids and diamonds using a count of spaces that changes from row to row. This lesson uses the same kind of count, n - 1 - i, and shows where it comes from. Each worked example below works out that count first, then prints the row.
Two names are used throughout. n is the number of rows in the pattern. i is the row number, starting at 0 for the top row. Python counts from 0 too, so with four rows the rows are numbered 0, 1, 2 and 3, and the bottom row is n - 1.
If you know Java, for i in range(n): does the job of for (int i = 0; i < n; i++). Python marks the loop body with indentation instead of braces.
The idea: one half, then its reflection
Imagine folding a sheet of paper down its middle. The two halves land on top of each other. The fold line is the mirror line. A pattern is symmetric when each row has the same shape on both sides of the mirror line.
Start with a small left-aligned triangle, with n = 4. The first row has one star, the second has two, the third has three and the fourth has four:
*
**
***
****This triangle grows to the right. Its left edge is straight, and its right edge is a slope. To mirror it, keep the same stars but move them to the right edge, so the spaces come first in each row. The spaces needed in a row depend only on the row number, and the formula is n - 1 - i. You can check it with n = 4:
| Row i | Spaces, n - 1 - i | Stars, i + 1 |
|---|---|---|
| 0 | 4 - 1 - 0 = 3 | 1 |
| 1 | 4 - 1 - 1 = 2 | 2 |
| 2 | 4 - 1 - 2 = 1 | 3 |
| 3 | 4 - 1 - 3 = 0 | 4 |
The top row has the most spaces and the bottom row has none. Each row down has one space fewer, because the stars get one longer. The number of spaces plus the number of stars is always n, so every row ends at the same column.
Counting with range
range is the Python tool for counting in a loop. range(4) gives the numbers 0, 1, 2 and 3. In general, range(k) gives k numbers, from 0 up to k - 1, so a loop over range(k) runs k times. range stops before the number you give it, so range(0) gives no numbers and its loop body never runs.
A string is text inside quotes, such as "*". Python can repeat a string with the * operator: "*" * 3 is "***", and "*" * 0 is an empty string.
Before you write the loops, say the rule out loud: "For each row i from 0 to n - 1, print n - 1 - i spaces, then print the stars for that row." This is the same method as the earlier lessons, with one extra loop for the spaces.
Worked example 1: the right-aligned triangle
Take the left-aligned triangle from the previous section and push it to the right. Each row gets its spaces on the left, and its stars then end at the right edge. This is the simplest mirror pattern, and it has one loop for the spaces and one loop for the stars.
The first loop prints the spaces. For row 0 it runs three times, because n - 1 - i is 3. The second loop prints the stars. range(i + 1) runs i + 1 times, which is one star in row 0 and four in row 3. print("*", end="") prints a star and keeps the cursor on the same line. The end part replaces the newline that print adds by default. print() with nothing inside ends the row.
The name _ marks a loop value that the body does not use. It is a convention, not a rule of the language.
The output is:
*
**
***
****Python can also build each row as one string and print it once. " " * (n - 1 - i) makes the spaces, "*" * (i + 1) makes the stars, and + joins the two strings into one:
It prints the same four lines. The later examples in this lesson use loops, because each loop shows one count on its own. The string version is shorter once the counts are clear.
Check the first and last rows by hand before you trust the loop. The top row must have three spaces and one star. The bottom row must have no spaces and four stars. Both are true here.
Worked example 2: a bowl with two sides
Now make the mirror on both sides. Each row has a left block of stars, a gap in the middle and a right block of stars. The blocks are the same size, and the gap is the same on both sides of the centre.
The left block has i + 1 stars, as before. The gap has to be the same on both sides, so the number of spaces in the gap is twice the spaces on one side. For row i, one side would need n - 1 - i spaces, so the gap is 2 * (n - 1 - i). The right block is printed the same way as the left.
The output is:
* *
** **
*** ***
********Each line is the same width. The width is (i + 1) + 2 * (n - 1 - i) + (i + 1), which simplifies to 2 * n. For n = 4 every line is eight characters wide. A quick way to check a symmetric pattern is to measure one line with len(line), which gives the number of characters in a string, and see whether the width matches the formula.
The middle of each line is blank, and the stars grow inward as you go down. Notice that the gap shrinks by two spaces each row, because it is 2 * (n - 1 - i) and n - 1 - i drops by one each row.
Worked example 3: a mirrored number pyramid
The third example is harder, because the mirror is made of numbers rather than stars. Each row counts up from 1 and then back down. Here is the target picture for n = 5:
1
121
12321
1234321
123454321Each row has three parts. First come the spaces, which are n - 1 - i as before. Then the left half counts up: 1, 2, and so on, up to i + 1. Then the right half counts down from i to 1. The right half is the left half reversed, but it does not repeat the centre number, so row 2 has 1, 2, 3 on the left and 2, 1 on the right.
range(1, i + 2) starts at 1 and stops before i + 2, so it prints the numbers 1 to i + 1. range(i, 0, -1) starts at i, moves down by 1 each time, and stops before 0, so it prints the numbers from i down to 1. The third number in range is the step. For row 0 the start is 0 and the stop is 0, so the loop gives no numbers. The row then prints only the single 1. print(j, end="") prints a number as text, so no conversion is needed.
The output is:
1
121
12321
1234321
123454321Every row is centred on the same column, which is column 4 for n = 5 when the first column is numbered 0. Digits are one character each here, so the centre lines up. If n were 10 or more, two-digit numbers would break the alignment. A later lesson on aligned tables covers that case, and this lesson stays with single digits.
How the three examples fit together
All three examples use the same first step. They print n - 1 - i spaces, and that puts the mirror line in the same place for every row. The second step changes: a right-aligned triangle prints one block of stars, a bowl prints two blocks with a gap, and the number pyramid prints two blocks of numbers. In each case, the second half is built from the same count as the first half, so the picture stays symmetric.
When you meet a new symmetric pattern, write the target picture for n = 3 on paper first. Then mark the mirror line, count the spaces from the outside edge of each row, and count the symbols on each side. The counts give you the loops.
Common mistakes
Using i for the spaces
The most common error is to write i where n - 1 - i belongs. The spaces then grow down the page instead of shrinking. Here is the space loop from worked example 1 with range(i) in place of range(n - 1 - i), for n = 4. The star loop stays the same:
n = 4
for i in range(n):
for _ in range(i):
print(" ", end="")
for _ in range(i + 1):
print("*", end="")
print()It prints:
*
**
***
****This is a staircase that leans to the right, not a mirror. The rows start at different columns and the top row is not in the middle. The fix is to count from the other end, so the top row gets the most spaces. Check the first row: range(n - 1 - 0) must give n - 1 values.
Using n - i instead of n - 1 - i
This is the off-by-one error in the spaces. With n - i, every row has one extra space. The pattern is still symmetric, but the whole picture moves one column to the right: the top row no longer starts in the middle column, and the bottom row starts at column 1 instead of column 0. Here is the number pyramid with range(n - i) for the spaces, for n = 5:
The output is:
1
121
12321
1234321
123454321The top row has six characters, and the bottom row has ten. In the correct version the top row has five characters and the bottom row has nine. Use len(line) on one row to find the error. If the top row is longer than n characters, the space count is one too big.
Forgetting the line break
Python's print ends each line by default. The end="" part removes that ending, so a program that uses end="" must also call print() at the end of each row. Here is the output for n = 3 with the final print() removed:
n = 3
for i in range(n):
for _ in range(i + 1):
print("*", end="")It prints:
******The stars are all there, but they are on one line. The output also ends without a line break, so the prompt appears right after the last star. The row loop is correct, and only the print() at the end of the outer loop is missing. A plain print("*") already ends its line, so this mistake only happens when end="" is used.
A missing colon
A syntax error is a mistake in the grammar of the code, such as a missing symbol. Python checks the whole file for syntax errors before it runs any of it. Every for line needs a colon at the end, because the colon marks where the loop body starts. This file leaves the colon out on line 2:
n = 4
for i in range(n)
print(i)Python prints this and does not run the file:
File "/path/to/file.py", line 2
for i in range(n)
^
SyntaxError: expected ':'The folder part of the path depends on where you save the file. The message says Python expected a colon on line 2. The caret points just after range(n), which is where the colon belongs. The fix is for i in range(n):.
A misspelled method name
A typing error in a method name is also an error in Python, but Python finds it only when that line runs. A method is a function that belongs to a value. join is a method of strings. This program writes jion by mistake:
n = 4
for i in range(n):
print(" ".jion(["*", "*"]))The program prints:
Traceback (most recent call last):
File "/path/to/file.py", line 3, in <module>
print(" ".jion(["*", "*"]))
^^^^^^^^
AttributeError: 'str' object has no attribute 'jion'A traceback is the list Python prints when a program stops with an error. The last line names the error. An AttributeError means Python looked for a name on a value and did not find it. Here the value is a str, which is Python's name for a string. The caret underlines the part of the line that Python could not use. Compare the name with the method you meant to use, join.
Practice
Exercise 1
Predict the output of the right-aligned triangle when n = 3. Then run it to check. Write each line exactly, including the spaces.
Show the answer
With n = 3, row 0 has two spaces and one star, row 1 has one space and two stars, and row 2 has no spaces and three stars. The output is:
*
**
***Exercise 2
Change the bowl from worked example 2 so that n = 3. Write the three lines of output.
Show the answer
For n = 3 the gap in row 0 is 2 * (3 - 1 - 0), which is four spaces. Row 1 has a gap of two spaces, and row 2 has no gap. The output is:
* *
** **
******Exercise 3
Predict the output of the mirrored number pyramid when n = 3.
Show the answer
Row 0 has two spaces and the number 1. Row 1 has one space and the numbers 1, 2, 1. Row 2 has no spaces and the numbers 1, 2, 3, 2, 1. The output is:
1
121
12321Exercise 4
A bowl is printed with n = 5. What is the width of each line? Write the number of characters in the top line and in the bottom line.
Show the answer
Each line is 2 * n characters wide, so each line is ten characters. len(line) gives 10 for every line. The top line is *, then eight spaces, then *, which is ten characters. The bottom line is ten stars. The output is:
* *
** **
*** ***
**** ****
**********Exercise 5
For n = 6, how many spaces does row 2 need in a right-aligned triangle? Show the formula and the number.
Show the answer
The formula is n - 1 - i. Here n is 6 and i is 2, so the count is 6 - 1 - 2, which is 3. Row 2 is the third row, so it has three spaces before its stars. A quick check: rows 0 to 5 have 5, 4, 3, 2, 1 and 0 spaces, so the third row from the top has three.
Summary
- A symmetric pattern is built from one half and its mirror image, so one rule can produce both halves.
- In a row with n rows, the top row is row 0, and the spaces needed on one side are
n - 1 - i. - A bowl has a gap of
2 * (n - 1 - i)spaces between two blocks ofi + 1stars. - Check a symmetric pattern by measuring the width of the first and last rows. A bowl is
2 * nwide. - The most common errors are using
iwheren - 1 - ibelongs, and the off-by-one errorn - i. Both shift the picture, and the rows no longer line up. range(k)gives k numbers, from 0 to k - 1, so a loop over it runs k times.