Python Tic Tac Toe Building Block Function Would appreciate any input on these building blocks functions of tic tac toe.

def display_field(cells):
    print('''\
---------
| {} {} {} |
| {} {} {} |
| {} {} {} |
---------\
'''.format(*cells))


def analyze_field(cells):
    """

    Args:
        cells (str): a string of an XO pattern

    Returns:
        (str): the result of the XO field
    """

    num_x = cells.lower().count('x')
    num_o = cells.lower().count('o')

    rows = [[cells[j] for j in range(i, i + 3)] for i in range(0, 9, 3)]
    columns = [[row[i] for row in rows] for i in range(3)]

    first_diagonal = rows[0][0] + rows[1][1] + rows[2][2]
    second_diagonal = rows[0][2] + rows[1][1] + rows[2][0]

    if abs(num_x - num_o) not in [0, 1]:
        return 'Impossible'

    num_winners = 0
    for row, column in zip(rows, columns):
        if len(set(row)) == 1:
            num_winners += 1
        if len(set(column)) == 1:
            num_winners += 1
    if num_winners &gt; 1:
        return 'Impossible'

    for row in rows:
        if len(set(row)) &lt;= 1:
            return f'{row[0]} wins'

    for column in columns:
        if len(set(column)) &lt;= 1:
            return f'{column[0]} wins'

    if len(set(first_diagonal)) &lt;= 1:
        return f'{first_diagonal[0]} wins'

    elif len(set(second_diagonal)) &lt;= 1:
        return f'{second_diagonal[0]} wins'

    # No winner
    else:
        if '_' in cells or ' ' in cells:
            return 'Game not finished'
        else:
            return 'Draw'


cells = input('Enter cells:')

display_field(cells)

print(analyze_field(cells))



Enter cells:XO_XO_XOX
---------
| X O _ |
| X O _ |
| X O X |
---------
Impossible
Flat array for display

It looks like display_field accepts a one-dimensional array. By the time you're manipulating it with business logic and presenting it back to the user, it should be a two-dimensional array. Rather than one format string, you would then call '\n'.join().

Presentation vs. business logic

In several places your presentation ("Impossible", "X") is all conflated with your business logic. Do not use strings to return status from analyze_field. What to use instead depends on a number of factors; some options are:


Use an Enum
Use a callback object where there is one function per result option


Along similar lines, do not store 'x' and 'o' strings in the cells. You can use an Enum here, perhaps with values PLAYER1/PLAYER2. Strings are unconstrained, and a matter of style/presentation/UI rather than business logic, which should be more verifiable.

Set membership

if abs(num_x - num_o) not in [0, 1]:


can be

if abs(num_x - num_o) not in {0, 1}:


since order does not matter and set lookup has O(1) time complexity. That said, I think this is equivalent to

if not(-1 &lt;= num_x - num_o &lt;= 1):


assuming that num_x and num_o are integral.

Zip

I question this logic:

for row, column in zip(rows, columns):
    if len(set(row)) == 1:
        num_winners += 1
    if len(set(column)) == 1:
        num_winners += 1


I think what you're looking for is a chain, not a zip:

for sequence in chain(rows, columns):
    if len(set(sequence)) == 1:
        num_winners += 1


In other words, there's no point to checking a row and its corresponding column in the same loop iteration.