1

I'm setting up a simple game and am having trouble accessing the Game class instance from the Player class instance:

class Player():
    global game
    print game.board  # NameError: name 'game' is not defined


class Game():
    def __init__(self):
        self.board = range(9)

    p = Player()


if __name__ == "__main__":
    game = Game()

I'm sure this is a simple fix/misunderstanding on my part, but what am I missing?

Debugging
  • 21
  • 2
  • 1
    You're not "accessing the Game class instance from the Player class instance." You're accessing it from the Player class *definition*, which is executed once when the class is defined. This code would have worked had it been defined inside one of Player's methods. Also suspect is the class level reference to Player `p` inside of the Game class definition. You probably need to move that to Game's `__init__` method. – Steven Rumbalski Feb 09 '15 at 04:21
  • It isn't working because ```game``` does not exist when the interpreter is creating ```Player```. You have a bit of a conundrum, each class has a reference to the other but because Python creates the objects sequentially, one will always not exist no matter how you rearrange the definitions. You need to re-think the design. – wwii Feb 09 '15 at 04:39
  • @StevenRumbalski You're right, the game.board access is in the Player class definition and not an instance. I was thinking in terms of once the Player class is instantiated it would be accessed, but my phrasing was incorrect. – Debugging Feb 09 '15 at 06:17

2 Answers2

3

The interpreter will first try to make class Player, then class Game and then run the code. So it will make class Player before game = Game() is executed and hence there is no variable game at that moment and cause your error.

Steven Rumbalski
  • 44,786
  • 9
  • 89
  • 119
dragon2fly
  • 2,309
  • 19
  • 23
  • 2
    @StevenRumbalski as I read it, you and dragon2fly are saying the exact same thing. The way you phrase it is clearer about the difference between defining a class and creating an instance of the class, but you're saying the same thing. – Dan Getz Feb 09 '15 at 04:36
  • @Steven Rumbalski I said the error occurs during the creation of class `Player` not when an instance of that class is created. – dragon2fly Feb 09 '15 at 04:38
  • So my misunderstanding is in the order that the interpreter processes the code. I was thinking it would start at the if __name__ == "__main__" condition, but that can't be correct because I can't define the Game class before the Player class and have this code work. The interpreter's definitely starting at the top of the file. – Debugging Feb 09 '15 at 06:21
2

A better approach is to use DI (dependency injection) in order to "pass" a Game object to Player upon init:

class Player(object):
    def __init__(self, game):
        self.game = game  

    def print_player(self):
        print self.game.board


class Game(object):
    def __init__(self):
        self.board = range(9)


if __name__ == "__main__":
    game = Game()
    player = Player(game)
    player.print_player() # prints [0, 1, 2, 3, 4, 5, 6, 7, 8]

Relying on globals() is not a good practice since it relies on the order of execution (like in your case), makes the code less readable (you have to jump from one place to another instead of reading it "fluently") as well as it might introduce side effects.

Bad practice (a.k.a. don't do it at home!):
The following code (though it's a bad practice) will work since the order of decelerations is meaningful when you're using globals(): game = Game() should be declared before class Player in order for it to be used there:

class Game():
    def __init__(self):
        self.board = range(9)

game = Game()

class Player():
    global game
    print game.board 

p = Player() # prints [0, 1, 2, 3, 4, 5, 6, 7, 8]
Community
  • 1
  • 1
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • 2
    @LegoStormtroopr a downvote means " not useful answer". Though it doesn't explain the source of the issue it explains how to avoid it by writing better code. Is that considered by you as not useful ? – Nir Alfasi Feb 09 '15 at 04:07
  • 1
    I knew that using global was a bad idea but the behavior still surprised me. This approach of passing the Player the Game instance feels weird to me; I think in terms of the Player being a part of the game and not vice versa. I do get what you're saying though, thanks for the feedback. – Debugging Feb 09 '15 at 06:25
  • @Debugging DI could be a bit confusing in the beginning, but when you get used to it it becomes natural. It has its advantages such as, it makes your code easier to test. – Nir Alfasi Feb 09 '15 at 06:44