# Expanding on a problem from Automate the Boring Stuff

Note: this is not the solution for the "Character Picture Grid" problem from Automate the Boring Stuff. Instead, this is an expansion of that problem simply for the sake of understanding nested loops, lists within lists, and iterating through values in order to print a picture on the screen.

Thanks to anyone who takes the time to review this. Should I comment more throughout my code so that it's easier to read? Are there ways to shorten this program so that it could run faster? I'm still quite new to programming.

# This is an expanded version of the Character Picture Grid
# problem from Al Sweigart's book, Automate the Boring Stuff With Python.
# This function takes in user input to determine what direction to print
# the arrow.

rightArrow = [['.', '.', '.', '.', '.', '.'],
['.', 'O', 'O', '.', '.', '.'],
['O', 'O', 'O', 'O', '.', '.'],
['O', 'O', 'O', 'O', 'O', '.'],
['.', 'O', 'O', 'O', 'O', 'O'],
['O', 'O', 'O', 'O', 'O', '.'],
['O', 'O', 'O', 'O', '.', '.'],
['.', 'O', 'O', '.', '.', '.'],
['.', '.', '.', '.', '.', '.']]

def printArrow(list):
rowLength = len(list[0])  # length of x plane coordinates
columnLength = len(list)  # length of y plane coordinates
print('Welcome to Arrow Pointer v1.0!')
while True:
#main program loop
print('\n\nType in one of the following:\n\nup, down, left, right, or end to exit the program.')
#user determines direction of arrow to be printed
userInput = input()
if userInput.lower() == 'right':
for i in range(columnLength):
for j in range(rowLength):
#first loop iterates through main list
#nested loop iterates through inner list elements
print(list[i][j] + '     ', end='')
print('\n')

if userInput.lower() == 'left':
for i in range(columnLength):
for j in range(rowLength - 1, -1, -1):
#iterate backwards to print arrow in opposite direction
print(list[i][j] + '     ', end='')
print('\n')

if userInput.lower() == 'down':
for i in range(rowLength):
for j in range(columnLength):
print(list[j][i] + '     ', end='')
print('\n')

if userInput.lower() == 'up':
for i in range(rowLength-1, -1, -1):
for j in range(columnLength-1, -1, -1):
print(list[j][i] + '     ', end='')
print('\n')

if userInput.lower() == 'end':
quit()

printArrow(rightArrow)


• Let's start with the basics. Always use if __name__ == "__main__": printArrow(rightArrow) instead of directly calling your function (printArrow(rightArrow)) to avoid running it if importing the function in the future from another module.
• A function called printArrow should print an arrow, not ask a user for input, print an arrow, and control the flow of the program. Divide it into def print_arrow(list, direction), main(), etc.
• Instead of using quit() which controls the global execution flow, you can use a plain return when inside a function. Cleaner and follows SRP.
• Naming! In python we don't usually use camelCase for functions; use instead: print_arrow. Same for variables like rowLength -> row_length.
• Pass your code through black or similar to have it nicely formatted (black.vercel.app/), otherwise the small human "errors" show up :)
• Constants in Python, by convention, use all upper case and don't need to be passed into functions (the only exception when globals are generally acceptable). So use rightArrow -> RIGHT_ARROW and def printArrow(list): -> def printArrow():.
• Use comments for the important stuff (the one which I can't trivially deduce from the code itself). What is this arbitrary amount of spaces? ' '? Put it into a constant and comment why that many if it has a meaning. E.g., SEPARATOR = ' '.
• There is some code repetition, can't you put this into a function def _print_arrow_auxiliary(x_start: int, x_end: int, x_step: int, y_start: int, y_end: int, y_step: int) -> None:?
for i in range(rowLength-1, -1, -1):
for j in range(columnLength-1, -1, -1):
print(list[j][i] + '     ', end='')
print('\n')

• It is a matter of taste, but type-hinting your functions does give you many advantages (self-explanatory code, better IDE highlights and suggestions, etc.)... def printArrow(list): -> def printArrow(list: List[List[str]]) -> None:.
• Thanks for this response. There are a few things I need clairification on. I don't understand the following line: name == "main": printArrow(rightArrow). Also, your second point about separating the function into print_arrow(list, direction), main(), etc. What is the purpose of 'main()' in this context? Sep 4, 2021 at 17:00
• First question is answered here, this is a basic in Python, so I suggest you spend some time and make sure you get it! As for separation functions, in general we try to follow SRP, each function should only do one thing. So print_arrow should do only that, print the arrow, not have the main program loop or ask the user for input. It is a convention to have a function called main() which has the program main loop. Sep 4, 2021 at 17:34
• Thanks! Last question, how did you determine that I had constants in my code? I'm not aware of the difference between a constant and a variable in Python. I know in JavaScript, the declaration is either var, const, or let, but as far as I know, every variable in Python is a variable. Sep 4, 2021 at 21:43
• Well, yes. In Python there are no constants. That is, there is no way to enforce a variable is actually constant and never changes. However, us, as ✨responsible programmers✨ do use some variables which we intend to use as constants. If we want to show that a variable is intended to be use as a constant, in Python we use all uppercase like SOME_CONSTANT. In your case, e.g., rightArrow is used but never modified, so let's make it explicit by using the naming convention for constants! Sep 4, 2021 at 22:22
• Maybe consider calling lower on user_input just once and storing it instead of repeating it. So: user_input = input().lower(). The rest seems fine! Nov 4, 2021 at 8:06