Search code examples
pythonlistif-statementreturntuples

Python different output for one-line if else return statement


While making a game of Connect Four, I encounter a strange problem in a function called make_move, when two equivalent return statements behaving differently.

The only direct dependency function is put_piece(board, column, player), which puts a player's piece in the bottommost empty spot in the given column of the board. put_piece returns a tuple of two elements: the index of the row the piece ends up in (or -1 if the column is full) and the updated board. The put_piece function is implemented correctly.

The make_move function is where the disagreement occurs. It successfully returns row (the index of the row the piece is placed in) and board (the updated board) if I implement using the usual if else return notation, as shown here:

def make_move(board, max_rows, max_cols, col, player):
    """
    Put player's piece in column COL of the board, if it is a valid move.
    Return a tuple of two values:

        1. If the move is valid, make_move returns the index of the row the
        piece is placed in. Otherwise, it returns -1.
        2. The updated board
    """
    if 0 <= col < len(board[0]):
        return put_piece(board, max_rows, col, player)
    return -1, board

This is how make_move should return:

>>> rows, columns = 2, 2
>>> board = create_board(rows, columns)
>>> row, board = make_move(board, rows, columns, 0, 'X')
>>> row
1
>>> board
[['-', '-'], ['X', '-']]

However, if I change make_move to

def make_move(board, max_rows, max_cols, col, player):
    """
    Put player's piece in column COL of the board, if it is a valid move.
    Return a tuple of two values:

        1. If the move is valid, make_move returns the index of the row the
        piece is placed in. Otherwise, it returns -1.
        2. The updated board
    """
    return put_piece(board, max_rows, col, player) if 0 <= col < len(board[0]) else -1, board

Both return values get assigned as a tuple to row, and board simply carries on the previous value.

>>> rows, columns = 2, 2
>>> board = create_board(rows, columns)
>>> row, board = make_move(board, rows, columns, 0, 'X')
>>> row
(1, [['-', '-'], ['X', '-']])
>>> board
[['-', '-'], ['-', '-']]

The two ways of writing the function are literally the same except notation. Any idea why this happens?


Solution

  • This is due to precedence. The comma has fairly low precedence, so

    put_piece(board, max_rows, col, player) if 0 <= col < len(board[0]) else -1, board
    

    is equivalent to

    ((put_piece(board, max_rows, col, player) if 0 <= col < len(board[0]) else -1), board)
    

    But you really want

    put_piece(board, max_rows, col, player) if 0 <= col < len(board[0]) else (-1, board)