Mike Nedelko
Mike Nedelko

Reputation: 709

"self" as method attribute

I am attempting to teach myself Python at the moment and I am trying to work my way through a python program, which is essentially a pacman game. Whilst doing so, I discovered the following class call which contains 'self' as a method attribute.

game = Game(agents, display, self, catchExceptions=catchExceptions)

THE CONTEXT:

This method call is part of the following function:

class ClassicGameRules:
    """
    These game rules manage the control flow of a game, deciding when
    and how the game starts and ends.
    """
    def __init__(self, timeout=30):
        self.timeout = timeout

    def newGame( self, layout, pacmanAgent, ghostAgents, display, quiet = False, catchExceptions=False):
    [1] agents = [pacmanAgent] + ghostAgents[:layout.getNumGhosts()] 
    [2] initState = GameState() 
    [3] initState.initialize( layout, len(ghostAgents) ) 
    [4] game = Game(agents, display, self, catchExceptions=catchExceptions) 
    [5] game.state = initState 
    [6] self.initialState = initState.deepCopy()

[1] Just to make this a little more palatable, 'agents' contain the pacman and ghost agent objects combined into a list of lists, which when printed looks something like this:

[<keyboardAgents.KeyboardAgent instance at 0x1004fe758>, <ghostAgents.RandomGhost instance at 0x1004fe518>, <ghostAgents.RandomGhost instance at 0x1004fe7a0>, <ghostAgents.RandomGhost instance at 0x1004fe950>, <ghostAgents.RandomGhost instance at 0x1004feb90>]

[2] initState = GameState(): GameState() is a data object containing all sorts of information about the Game state

[3] initState.initialize( layout, len(ghostAgents) ): initialises the very first game state from a layout array. This layout array lets the program know where the pacman and ghosts spawn, where the food and capsules are displayed and where walls can be found.

[4] game = Game(agents, display, self, catchExceptions=catchExceptions): The Game class manages the control flow, soliciting actions from agents. Agents can be a keyboard agent (controlled by the player, who controls the pacman) and automated agents controlling the ghosts (see [1]).

[5] game.state = initState: copies the content of [2] into the variable game.state.

[6] self.initialState = initState.deepCopy(): deepCopy is a function which runs a copy operation on variables which define the game state of the game board, such as the layout or the position of the food.

CLASS GAME: Here is the code of the class Game:

class Game:
"""
The Game manages the control flow, soliciting actions from agents. 
"""

def __init__( self, agents, display, rules, startingIndex=0, muteAgents=False, catchExceptions=False ):
    self.agentCrashed = False
    self.agents = agents                                           ## agents contain the pacman and ghost agent objects combigned into a list of lists agents = [pacmanAgent] + ghostAgents[:layout.getNumGhosts()]
    self.display = display                                        
    #print("This is the display object" + str(self.display))
    self.rules = rules                                             ## The rules are passed on as the self attribute of the ClassicGameRules class within the call of Game in newGame [REF 115].
    self.startingIndex = startingIndex                             ## starting index is given as default attribute by the __init__ method.
    self.gameOver = False
    self.muteAgents = muteAgents
    self.catchExceptions = catchExceptions
    self.moveHistory = []
    self.totalAgentTimes = [0 for agent in agents]                 ## This creates a list that replaces the list elements of the agents list, which contains the agent objects, with zeros. It looks something like this [0,0,0]
    ##print(self.totalAgentTimes)
    self.totalAgentTimeWarnings = [0 for agent in agents]          
    self.agentTimeout = False
    import cStringIO                                              
    self.agentOutput = [cStringIO.StringIO() for agent in agents] 

WHAT CONFUSES ME

When game = Game(agents, display, self, catchExceptions=catchExceptions) is called, "self' is passed on to Game as an attribute. In Game the data contained within "self" takes is placed within the "rules" variable.

But does does this variable contain?

Intuitively I would suggest that it would be the "self" instance of the newGame object. But seeing how this method is called self.initialState = initState.deepCopy(), which doesn't seem to make sense...

Or are we referencing the self - object of the ClassicGameRules class?

I know this was a handful of text to read, but I wasn't quite sure how to shorten this question. Please let me know if more information is required. Any help would be highly appreciated. :)

Z_101

Upvotes: 0

Views: 306

Answers (2)

jonrsharpe
jonrsharpe

Reputation: 121974

The line:

game = Game(agents, display, self, catchExceptions=catchExceptions) 

in ClassicGameRules.newGame calls Game.__init__ with three positional arguments and one keyword argument, plus the implicit first positional argument of the new Game instance. Game.__init__ is defined as:

def __init__(self, agents, display, rules, startingIndex=0, muteAgents=False, catchExceptions=False ): 
                 # agents  display  self   [default]        [default]         catchExceptions

Crucially, the self in the calling function is not the same as the self in the called function. This call passes the ClassicGameRules instance as the argument rules to the Game instance. This creates a two-way link:

  • You can access the Game instance within ClassicGameRules instance methods via self.game; and
  • You can access the ClassicGameRules instance within Game instance methods via self.rules.

The next part:

game.state = initState 
self.initialState = initState.deepCopy()

sets the state attribute of the Game instance to initState, but then saves a copy of that state to self.initialState (where self refers to the ClassicGameRules instance, as we're inside a ClassicGameRules instance method).

Upvotes: 1

Maxime Lorant
Maxime Lorant

Reputation: 36141

In the method newGame of the class ClassicGameRules, self represents the current instance of the class:

game = Game(       agents, display, self, ...) 
                      v       v       v
def __init__(self, agents, display, rules, ...)

The first self of the __init__ method here is to represents to the new object that will be created in the Game class.

In your case, the Game class has in rules a reference to the instance of ClassicGameRules which called the constructor of the Game instance. The deepCopy after has nothing to do and is just to synchronized the initial state of both objects, by coping the state of the object just created (game) into the "parent" object (instance of ClassicGameRules).

Upvotes: 1

Related Questions