__str__ method for a tic-tac-toe board

I am making my first object-oriented program in python and I am having trouble with the __str__ part of Board() class. I want it to be a visual representation of the tic-tac-toe board. I have achieved this but the code looks ridiculous and I know there must be a better way! Here is my project so far:

class Space(object):

def __init__(self, state="x"):
self.state = state

state_names = ['empty', 'x', 'o']

def __str__(self):

return str(self.state)

def change_state(self, string):
if string == 'x':
self.state = 'x'
else:
self.state = 'o'

class Board(object):

def __init__(self):
self.spaces = []
for num in range(0,9):
space = Space()
self.spaces.append(space)
board = self.spaces

def __str__(self):
res = []
for space in self.spaces:
res.append(str(space))
return '   |   |'  + '\n ' + res[6] + ' | ' + res[7] + ' | ' + res[8] + '\n   |   |' + '\n-----------' + '\n   |   |' + '\n ' + res[3] + ' | ' + res[4] + ' | ' + res[5] + '\n   |   |' + '\n-----------' + '\n   |   |' + '\n ' + res[0] + ' | ' + res[1] + ' | ' + res[2] + '\n   |   |'


the default output should (and does) look like this:

   |   |
x | x | x
|   |
-----------
|   |
x | x | x
|   |
-----------
|   |
x | x | x
|   |


migrated from stackoverflow.comJul 3 '16 at 10:30

This question came from our site for professional and enthusiast programmers.

Though not according the DRY principle and debatable for PEP8, I would do something like (Readability counts):

def __str__(self):
s = ('   |   |   \n' +
' # | # | # \n' +
'   |   |   \n' +
'---+---+---\n' +
'   |   |   \n' +
' # | # | # \n' +
'   |   |   \n' +
'---+---+---\n' +
'   |   |   \n' +
' # | # | # \n' +
'   |   |   '  ).replace('#', '{}')
return s.format(* map(str, self.spaces))


(btw.: I messed up the order of self.spaces)

(note: the asterisk in s.format(* map(... unpacks the list returned by map into individual arguments for format)

A bit more 'DRYer' solution would be:

    l1 = '   |   |   '
l2 = ' {} | {} | {} '
l3 = '---+---+---'
s = '\n'.join([l1, l2, l1, l3, l1, l2, l1, l3, l1, l2, l1])

• Quite readable! I highly recommend the first approach over the second though, although perhaps using a multi-line string instead. If the layout would have to change, this is easy to change. The "DRY" (in quotes, as I do not believe it to be DRY) approach would be harder to update. – Sjoerd Job Postmus Jul 3 '16 at 16:28

You can hugely reduce your __str__ using loops, e.g. using join() and a couple of generator expressions:

def __str__(self):
return '\n-----------\n'.join('   |   |\n '+' | '.join(str(self.spaces[x]) for x in [0+y, 1+y, 2+y])+'\n   |   |' for y in [6, 3, 0])


Output:

   |   |
x | x | x
|   |
-----------
|   |
x | x | x
|   |
-----------
|   |
x | x | x
|   |