Tic Tac Toe in Python 3.x
Naming of variables is perfect and you are following the python convention - and that is really good because it makes the code easier to read.
I've executed the code and I've noticed that when you insert an invalid input two things may happen

Input &quot;1A&quot;; Output:  &quot;Invalid format. That spot is already taken.&quot;
Input &quot;X&quot;; Output: Application crash

So that's a bug that we need to address.
Biggest problems in the code are

Use of global scope (that is, putting variables outside of methods)
Code duplication

About the first one, we need to put everything inside a method. This is better because you will constraint the scope of variables, make things easier to read and the intention of each line will be clearer. Also it's always better to define all the methods at the beginning and then the code that needs to be executed (that should be in a main method, normally).
If I would do it I would create a Board class but there is no particular reason to use Object Oriented Programming here so I will try to follow your way of reasoning.
At first we are initializing the board so
def create_board():
    board = []
    for _ in range(3):
        board.append([' '] * 3)
    return board

You were not actually using the x variable in the loop so I've renamed to _, which is a convention to say that you're not actually interested in that variable. To have magic numbers, like 3 in this case, is bad because someday you may ask &quot;Why 3?&quot;. Now it pretty obvious, but it might get more complex, an easy way to fix that is
def create_board(size=3):
    board = []
    for _ in range(size):
        board.append([' '] * size)
    return board

The behavior is the same but now everybody will know what is 3. Is quite useless to have a parametric sized board, because you can play Tic-Tac-Toe only with 3x3 boards, but I'm using that as a way to show you how to be prepared for later improvements (Connect-Four? :) ).
Now let's print the board. You're code was actually pretty good, I wanted to add a variable size board because, in your code, you're using the fact that the board is 3x3 in many hidden ways. (For example if dashes &lt;= 1:).
def print_board(board):
    board_size = len(board)
    cols = [chr(i) for i in range(ord('A'), ord('A') + board_size)]
    print()
    print(f&quot;    {'   '.join(cols)}&quot;)
    print()
    row_label = 1
    for row in board:
        print(row_label, ' ', ' | '.join(row))
        if row_label &lt; board_size:
            print(' ', ' -' * (2 * board_size))
        row_label += 1
    print()

x became row_label because that what it is. I've added the print() before and after, because they are, in fact, part of the print of the board.
The split method is not useful, characters in string can be already accessed by index. So we can get rid of it and the game will work as before.
As I was saying, there a few duplications in the code, and they are in the game loop, let's try to refactor that part, starting from the user input:
def ask_value(player_name, player_symbol, board):
    board_size = len(board)
    while True:
        player_input = input(f&quot;{player_name}, where would you like to place a {player_symbol}&quot;).lower()
        try:
            col_let = player_input[0]
            row = int(player_input[1]) - 1  # [REVIEW COMMENT] we use 0 based indexes, the user doesn't
            col = int(ord(col_let) - ord('a'))
            if (not (0 &lt;= col &lt; board_size)) or (not (0 &lt;= row &lt; board_size)):
                raise ValueError(&quot;Index out of board&quot;)
            if board[row][col] != ' ':
                print('That spot is already taken.')
                raise ValueError(&quot;Spot already taken&quot;)
            return row, col
        except (ValueError, IndexError):
            print('Invalid format.')

This method will replace the duplication of code for the input. I've added a range check to avoid going outside of the board and now the input validation bug is fixed.
Avoid to use non-zero based index; it is not the normal approach and you will get rid of all those - 1 all over the code.
The part where you check who won is a bit cumbersome and is using more developer time than cpu time (how long it took to check all the boolean conditions? :) ) also it doesn't scale with the board size (I understand that this wasn't a problem for you).
I'm not going to write how to check who won but I will suggest you a way to structure your code:
def check_winner(board, symbol):
   if check_rows(board, symbol):
       return symbol
   if check_cols(board, symbol):
       return symbol
   if check_diagonals(board, symbol):
       return symbol
   if check_tie():
       return '#'
   return None


So the main loop will become:
def main():
    board = create_board()
    print('This is Tic Tac Toe.')
    print('Player 1 will be X and Player 2 will be O.')
    players = [{&quot;name&quot;: &quot;Player 1&quot;, &quot;symbol&quot;: &quot;X&quot;}, {&quot;name&quot;: &quot;Player 2&quot;, &quot;symbol&quot;: &quot;O&quot;}]
    end = False
    while not end:  # [REVIEW COMMENT] this is more pythonish than &quot;win != True&quot;
        print_board(board)
        for player in players:
            symbol = player[&quot;symbol&quot;]
            row, col = ask_value(player[&quot;name&quot;], symbol, board)
            board[row][col] = symbol
            winning_symbol = check_winner(board, symbol)
            if winning_symbol == '#':
                print(&quot;It is a tie!&quot;)
                end = True
                break
            elif winning_symbol:
                print(f&quot;{player['name']} has won!!&quot;)
                end = True
                break

if __name__ == '__main__':
    main()