A beginner's first Tic Tac Toe implementation I've been teaching myself Python and decided to make a Tic Tac Toe game for a bit of practice. Any criticisms or pointers are welcome!
class TicTacToe:
  &quot;&quot;&quot;A class for playing tic tac toe&quot;&quot;&quot;

  def __init__(self):
    self.values = [&quot;-&quot;, &quot;-&quot;, &quot;-&quot;, 
                  &quot;-&quot;, &quot;-&quot;, &quot;-&quot;, 
                  &quot;-&quot;, &quot;-&quot;, &quot;-&quot;]
    self.player = &quot;x&quot;


  def show_board(self):
    print(f&quot;{self.values[0]} | {self.values[1]} | {self.values[2]}&quot;)
    print(f&quot;{self.values[3]} | {self.values[4]} | {self.values[5]}&quot;)
    print(f&quot;{self.values[6]} | {self.values[7]} | {self.values[8]}&quot;)


  def play_game(self):
    # display initial board
    self.show_board()
    # play for 9 turns max
    for i in range(9):
      self.handle_turn(self.values)
      if self.check_tie() or self.winner():
        break


  def handle_turn(self, values):
    # get next move from a player
    try:
      turn = int(input(f&quot;Player {self.player} pick a square (1-9) from left to right: &quot;))
    except ValueError:
      self.handle_turn(self.values)
      return
    
    # change values to show the move made (check square empty)
    if self.values[turn-1] == &quot;x&quot; or self.values[turn-1] == &quot;o&quot;:
      print(&quot;That square has been played already!&quot;)
      self.handle_turn(self.values)
    else:
      self.values[turn-1] = self.player
      self.show_board()
    # switch turns
    self.flip_player()


  def winner(self):
    # check all possible win methods
    self.row_winner = self.check_rows()
    self.column_winner = self.check_columns()
    self.diagonal_winner = self.check_diagonals()
    #declare a winner
    if self.row_winner:
      winner = self.check_rows()
      print(f&quot;{winner} has won the game!&quot;)
      return True
    elif self.column_winner:
      winner = self.check_columns()
      print(f&quot;{winner} has won the game!&quot;)
      return True
    elif self.diagonal_winner:
      winner = self.check_diagonals()
      print(f&quot;{winner} has won the game!&quot;)
      return True


  def check_rows(self):
    # Check for a win in the rows
    row1 = self.values[0] == self.values[1] == self.values[2] != &quot;-&quot;
    row2 = self.values[3] == self.values[4] == self.values[5] != &quot;-&quot;
    row3 = self.values[6] == self.values[7] == self.values[8] != &quot;-&quot;
    # Return the player that has won
    if row1:
      return self.values[0]
    elif row2:
      return self.values[3]
    elif row3:
      return self.values[6]  
  

  def check_columns(self):
    # Check for a win in the columns
    col1 = self.values[0] == self.values[3] == self.values[6] != &quot;-&quot;
    col2 = self.values[1] == self.values[4] == self.values[7] != &quot;-&quot;
    col3 = self.values[2] == self.values[5] == self.values[8] != &quot;-&quot;
    # Return the winning player
    if col1:
      return self.values[0]
    elif col2:
      return self.values[1]
    elif col3:
      return self.values[2]


  def check_diagonals(self):
    # Check for win in the diagonals
    dia1 = self.values[0] == self.values[4] == self.values[8] != &quot;-&quot;
    dia2 = self.values[2] == self.values[4] == self.values[6] != &quot;-&quot;
    # Return the player that has won
    if dia1:
      return self.values[0]
    elif dia2:
      return self.values[2]


  def check_tie(self):
    if &quot;-&quot; not in self.values:
      print(&quot;Game is a tie!&quot;)
      return True
    else:
      return False


  def flip_player(self):
    if self.player == &quot;x&quot;:
      self.player = &quot;o&quot;
    else:
      self.player = &quot;x&quot;

if __name__ == &quot;__main__&quot;:
  new_board = TicTacToe()
  new_board.play_game()
Visuals
You can try
self.values = [[&quot;-&quot; for _ in range(3)] for _ in range(3)]

This will let you set up a 3x3 grid without having to draw the whole thing.
Now you can also do
    def show_board(self):
        board_str = &quot;\n&quot;.join([ &quot; | &quot;.join(row) for row in grid ])
        print(board_str)

I would also suggest doing away with the show_board function altogether and instead use (for printing)
    def __str__(self):
        return &quot;\n&quot;.join([ &quot; | &quot;.join(row) for row in grid ])

and/or (for printing in interactive console)
    def __repr__(self):
        return &quot;\n&quot;.join([ &quot; | &quot;.join(row) for row in grid ])

Then you can directly do
xoxo = TicTacToe
print(xoxo)

and inside the class
print(self)

Data Representation and Input
I'm repeating myself here, but it doesn't make sense to have values be a 1-dimensional array. It's a tic-tac-toe board... it should be a 2D 3x3 grid or board. Also you should probably rename the variable values to grid or board.
This will mean taking 2 values as input from user in handle_turn(). I suggest not putting a large string inside input() as it's hard to read. Try instead
print(&quot;Enter space separated co-ordinates to pick a square&quot;)
print(f&quot;Player {self.player} pick a square: &quot;))
x = int(input())
y = int(input())

Ok, I'll admit this is a little annoying for the players.... maybe you could write the coordinates in the empty cells like
 X  | 1,2 |  O
2,1 |  X  | 1,3
3,1 | 3,2 |  O

(try to do this without writing out the whole starting grid manually, it should be possible using &quot;,&quot;.join(), str() and list comprehensions)
Also it should be possible to modify the row/column/diagonals functions with 2D indices like grid[0][0], grid[1][1], grid[2][2], etc. to make it more readable. Due to the way self.grid is stored, the row function can be simplified... but it will look very different from column/diagonal and so it's probably not worth it.