3
\$\begingroup\$

I have a while loop below, but I'm not quite sure if it's the most 'pythonic' thing. I've omitted most of the code, and have the skeletal structure below (but have all the code necessary to understand my game_status() function). And the main thing in question is whether/how to use a function as a condition in a while statement. (In my case, the game_status() function).

#Legal board positions
BOARD_POSITIONS = list(range(1, 10))

#All possible combinations of board positions giving 8 winning lines.
WINNING_LINES = [[1, 2, 3], [4, 5, 6], [7, 8, 9],
                 [1, 4, 7], [2, 5, 8], [3, 6, 9],
                 [1, 5, 9], [3, 5, 7]]

def game_status(board):
    """Updates the status of the game based on three outcomes:
       Returning 'X' or 'O' is either has three in a row; 'tie' if the
       board is full, but no players have won; or 'unfinished' otherwise"""
    for i, j, k in WINNING_LINES:
        if not available(board, i) and board[i] == board[j] == board[k]:
            return board[i]
    if any(available(board, i) for i in BOARD_POSITIONS):
        return 'unfinished'
    return 'tie'

while True:
    game_board = (' ' for i in range(10))
    while game_status(game_board) is 'unfinished':
        #Main game code
    else:
        if game_status(game_board) is 'X':
            #Code for X winning goes here
        elif game_status(game_board) is 'O':
            #Code for O winning goes here
        else:
            #Code for a tie goes here

As you notice, I've got to call game_status() multiple times in the else part of my while statement in order to check the value of game_status() so that I know to print who won or if the game was a tie.

One idea I had is the following:

else:
    status = game_status(game_board)
    if status = 'X':
        #Code
    elif status = 'O':
        #Code

and so on. However, I am quite curious as to the most pythonic way to do things, as well as whether or not using a function as a conditional is pythonic.

\$\endgroup\$
  • \$\begingroup\$ Okay. :O I know that on CodeReview, I was previously criticized for calling a function repeadetly in this manner, so I was curious if there was a simpler way, or more pythonic way. :P I'll check out codereview though! \$\endgroup\$ – Gavin Feriancek Nov 9 '13 at 1:11
  • 2
    \$\begingroup\$ You should probably get out of the habit of caring quite so much whether something is "pythonic" or not. Most of the time if you ask someone for an opinion on that, they will answer with their own preferred style. Almost nobody says, "I would do this this way, which I prefer, but the other way is more pythonic". Which means that almost nobody has an objective idea what pythonicism is. What you're asking for, is an opinion on code style. \$\endgroup\$ – Steve Jessop Nov 9 '13 at 1:22
  • 2
    \$\begingroup\$ What I can say objectively, is that I don't believe you're guaranteed that those is checks work. As it happens, CPython (and presumably other Python implementations) will use the same object for equal string literals in different places, but I don't believe the language guarantees that this will always be the case stackoverflow.com/questions/17679861/does-python-intern-strings \$\endgroup\$ – Steve Jessop Nov 9 '13 at 1:31
3
\$\begingroup\$

When you make "the same" function call repeatedly, you can create a question in the reader's mind whether or not it will return the same value every time (especially where the arguments are mutable objects, but even if not).

This is probably to be avoided where it's easy to do so. In your code the expectation is that the function will eventually change what value it returns to terminate the loop, and then won't change value across the if ... elif

Separately, there is a question of the performance of the code. Strictly speaking it is premature optimization to consider the performance of a function without measuring it to determine whether or not it is significant, but I think most programmers would avoid executing game_status more times than they have to.

On this occasion both concerns are dealt with much the same way -- assign the value to a variable and use the variable repeatedly. This minimizes the number of calls made for performance, and it indicates to the reader when you think you've changed something such that the result might have changed.

There is nothing inherently wrong with using the result of a function call as a condition:

if "foo".startswith("f"):
    print('as expected')

Out of interest - what is the condition in the "main game code" that will break out of the loop, avoiding the loop's else clause? Match abandoned?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.