Calling print
is very slow, you should focus on building strings then have one print. Fortunately Python has some really nice string manipulation options.
for col in range(upper_mid-1, row, -1):
print(end=" ")
You should rename col
to _
, which is standard nomenclature to say the variable is thrown away.
Since the print isn't based on col
walking up (step=1
) would be easier to understand.
for _ in range(row, upper_mid - 1):
We can instead use *
with a string and an integer to duplicate the string that amount of times.
output = " " * (upper_mid - 1 - row)
Lets convert your code to remove the range
s, and a number of prints.
I'm also remove the whitespace at the end of lines, something which you should never have.
num_rows = int(input())
if num_rows % 2 != 0:
upper_mid = math.ceil(num_rows/2)
lower_mid = num_rows//2
#prints upper normal triangle
for row in range(0, upper_mid):
print(
" " * (upper_mid - 1 - row)
+ "*" * (row + 1)
+ "*" * row
)
# prints lower inverted triangle
for row in range(0, lower_mid):
print(
" " * (row + 1)
+ "*" * (lower_mid - row)
+ "*" * (lower_mid - 1 - row)
)
else:
print("Only odd numbers!")
We can see the two loops for the upper and lower triangle are very similar.
If we change the lower triangle to walk from lower_mid - 1
down, then the inner parts would be the same.
We can also see we can merge the two lines creating the stars with row * 2 + 1
num_rows = int(input())
if num_rows % 2 != 0:
upper_mid = math.ceil(num_rows/2)
lower_mid = num_rows//2
for row in range(0, upper_mid):
print(
" " * (upper_mid - 1 - row)
+ "*" * (2 * row + 1)
)
for row in reversed(range(0, lower_mid)):
print(
" " * (upper_mid - 1 - row)
+ "*" * (2 * row + 1)
)
else:
print("Only odd numbers!")
I wouldn't expect a beginner to know these.
But there are some more advanced improvements we can make:
- Making a function, to increase reusability of the code.
- Using
itertools.chain
to let us not write the same body for the for loops twice.
- Changing
num_rows % 2 != 0
to a guard clause to reduce the arrow anti-pattern.
- We know
lower_mid = upper_mid - 1
, so we can replace upper_mid - 1
with lower_mid
and remove the need for using math.ceil
.
- Use
"\n".join
and a generator expression to return just a string to be printed.
- Use typehints to document the types the function takes and returns.
import itertools
def diamond(size: int) -> str:
if size % 2 == 0:
raise ValueError("only odd numbers are allowed")
mid = size // 2
return "\n".join(
" " * (mid - half_width) + "*" * (2 * half_width + 1)
for half_width in itertools.chain(
range(0, mid + 1),
reversed(range(0, mid)),
)
)
print(diamond(int(input("Size of diamond: "))))