My code behaves correctly with the output that Codeacademy expects, but I don't understand WHY it works. The way I currently understand the for loop is as such: The loop creates the variable row, that calls the variable board that has appended to it 5 lists of "O"'s (located within the list board). Row prints out everything it iterates over in board, which would be every "O" that is located in the lists in board.
board = 
for i in range(0,5):
zero = ["O"] * 5
for row in board:
As far as I understood the way you understood for loop is correct. At first you create a List variable named board. Then you add 5 Lists to this List variable, so each element of our board variable is a List itself and it is ["O","O","O","O","O"]. After that in the print_board function you call for a for loop. When you say for row in board: the variable row is/are the elements of our variable board. That means in first iteration row is the first element of our board variable which is ["O","O","O","O","O"]. In second iteration row is now the second element of our variable board which is the same. You are calling for the elements of variable board and printing them, and each of these rows are ["O","O","O","O","O"]. I hope I was able to express myself.
"Calling" suggests invoking a function but a variable isn't a function - you can't ever call a variable. And really you should think of them as names, not variables. Because they are not allocated memory, it's just a name which refers to some object.
So you've got a name, board, and this name refers to a value. That value is a list. Lists are not callable either, so you're not calling board, not itself or its value.
What the loop does, is to iterate through the value of board. Since board is a list, it will produce each value in the list, one at a time as you iterate through board. Each value in board is also a list, since we put lists in our list, which is what makes it two-dimensional.
Each new iteration, the next value is obtained from board, and the name row is made to refer to that value, because that's the name you specified in the for-loop. "Assign each value of board into row"
And then you print the value of row - which will involve converting it and its contained values, to string.
Finally something can be shown in the console, and then the loop moves on to next value in board
Words matter. Details matter. The computer does exactly as it is told so if you get a detail off and it's repeated a million times then you'll end up with a huge difference.