Rudziankoŭ
Rudziankoŭ

Reputation: 11251

Initialization of new instance in Python

I am writing unit tests fro tic-tat-toe on Python. And I have been extremely confused when noticed that my play object doesn't reinstantiate itself every method.

Here what I'm talking about:

def test_satisfactory_field_occupation(self):
        play = tictactoe.Play()
        play.make_move("+", 1, 1)
        self.assertEqual(play.check_satisfaction(1, 1), "Field has been already occupied, try again")

def test_satisfactory_success(self):
        play = tictactoe.Play()
        self.assertEqual(play.check_satisfaction(1, 1), "Ok") 

And I caught exception:

FAIL: test_satisfactory_success (__main__.TestPlay)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/sergei_rudenkov/PycharmProjects/hello_world/tic-tac-toe/tictactoe_test.py", line 23, in test_satisfactory_success
    self.assertEqual(play.check_satisfaction(1, 1), "Ok")
AssertionError: 'Field has been already occupied, try again' != 'Ok' 

The Play class is:

class Play(object):
    game = [['-', '-', '-'],
            ['-', '-', '-'],
            ['-', '-', '-']]

    move_count = 1
    finished = False

    def __str__(self):
        return "\n".join(map(str, self.game))

    def check_finished(self):
        result = False
        for i in range(2):
            if self.game[i][0] == self.game[i][1] == self.game[i][2] != '-':
                result = self.game[i][0]
            elif self.game[0][i] == self.game[1][i] == self.game[2][i] != '-':
                result = self.game[i][0]
        if self.game[0][0] == self.game[1][1] == self.game[2][2] != '-':
            return self.game[0][0]
        elif self.game[0][2] == self.game[1][1] == self.game[2][0] != '-':
            return self.game[0][2]
        elif not any("-" in row for row in self.game):
            return "Draw"
        else:
            return result

    def make_move(self, sign, x, y):
        self.game[x][y] = sign
        self.move_count += 1
        self.finished = self.check_finished()
        print self

    def check_satisfaction(self, x, y):
        try:
            x, y = int(x), int(y)
        except ValueError:
            return "Please enter integers, try again"
        if not (0 <= x <= 2 and 0 <= y <= 2):
            return "Arguments greater then 2 or less then 0 are not allowed, try again"
        if self.game[x][y] != '-':
            return "Field has been already occupied, try again"
        return "Ok"

    def winner(self):
        if self.finished == '+':
            return "First player (+) has won!"
        elif self.finished == '0':
            return "Second player (0) has won!"
        elif self.finished == 'Draw':
            return "The result is draw!"

Please, understand me correctly: I came from java and was considered each method has it's own stack but what I am seeing highly amazes me. Could someone help me to understand what is happening?

Upvotes: 3

Views: 128

Answers (2)

Marco Giordano
Marco Giordano

Reputation: 610

You are declaring you list "game" as "static". Means that each instance will share the same list. Move the "game" declaration inside the constructor, and then you should be fine.

class Play(object):
   def __init__(self):    

        self.game = [['-', '-', '-'],
            ['-', '-', '-'],
            ['-', '-', '-']]

The reason for that is that when you declare the list at class level, that list gets allocated at parsing time, when they generate the "class object", you could access the game list with Play.game. This should already give you an idea of the scope of the game list. Here a simplified example of what is happening with a list declared at class level:

class Play:
    game =[0]


p1 = Play()
print p1.game
p1.game[0] =1
p2 = Play()
print p2.game

Upvotes: 4

user5547025
user5547025

Reputation:

class Play(object):
    def __init__(self):
        self.game = [['-', '-', '-'],
                    ['-', '-', '-'],
                    ['-', '-', '-']]

        self.move_count = 1
        self.finished = False

Make sure you access these member variables using the self. prefix always in all other methods of the class:

  • self.game
  • self.move_count
  • self.finished

Take a look at the Python tutorial: 9.3.5. Class and Instance Variables.

Upvotes: 4

Related Questions