"self" as method attribute - python

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

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).

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).

Related

Python get parent class

I have a Maze class, and I have a MazeTilePlacer (MTP) class. The MTP class instances will be created inside the Maze class, and need access to a variable named emptytiles in the maze class, defined as self.emptytiles = []. How can I access the parent object from the MTP instance to access this variable?
You can handle this by giving each instance of MazeTilePlacer a reference to the same emptytiles list as your Maze object has. The Maze object can then delegate the responsibility of placing tiles to the other objects.
class Maze:
def __init__(self):
self.emptytiles = []
def place_tiles(self):
placer = MazeTilePlacer(emptytiles=self.emptytiles)
placer.do_placements()
class MazeTilePlacer:
def __init__(self, emptytiles):
self.emptytiles = emptytiles
def do_placements(self):
self.emptytiles.append('Another tile my Sir')
maze = Maze()
maze.place_tiles()
print(maze.emptytiles)
If you want to have multiple tile placers, you can all give them the same reference for emptytiles. If you need these to run in parallel and manipulate the emptytiles structure in a thread safe manner (i.e. where you need to have multiple accesses without anyone changing the structure under you - for example pop, do something, then append without something being popped or appended in between) you'll have to wrap it in a lock.

What is the most efficient way to call an object's state specific code block?

I've got a big list (30-50) of game objects that change between 5-7 states in a sudo-sequential order that radically alters their behavior.
My initial naive approach was to give each game object a State variable and then every action command would take it through an if-else block to find the correct action corresponding to state.
Option 1 (Naive Approach)
class Bot:
def action(self):
if self.state == 1:
action1()
if condition1_to_2():
self.state = 2
elif self.state == 2:
action2()
#...
list_of_bots = [Bot(), Bot(), ...]
for bot in list_of_bots:
bot.action()
But I thought, with 50 game objects, doesn't this if-else traversing begin to take up significant time?
Couldn't I speed up performance by going straight to state's correct code?
The only way I can think of implementing this is to use inheritance (either an interface or subclasses) and make each game state of each game object its own subclass, destroying and creating a new object every time it changes state.
Option 2 (Inheritance)
class Bot:
def action(): raise NotImplementedError
class Bot1(Bot):
def action():
action1()
class Bot2(Bot):
def action():
action2()
#...
list_of_bots = [Bot1(), Bot2(), ...]
for bot in list_of_bots:
bot.action()
This way each game object wouldn't have to check what state it was in every tick of the game which saves like 300 operations per tick.
My only problem is that it feels a bit like overkill making so many new classes. Is there a more compact but equally efficient solution to this problem?
I thought a state dictionary or list (assuming those give an O(1) access time), but how can you link a list to a code block?
Option 3 (Impossible?)
class Bot:
def __init__(self):
self.action_list = [action1(), action2(), ...]
self.state = 1
def action():
self.action_list[self.state]
The solution must be done in raw python3 (no pip install).
Your Option 3 can work with function references, however I am not sure if this program structure is the best to use. But to accomplish storing a list of function references you can do so like this:
class Bot:
def __init__(self, game_map):
self.action_list = [self.action1, self.action2] # Stores function references
self.state = 1
self.game_map = game_map # Pass in game_map object that the class can use
def action(self):
self.action_list[self.state]() # Calls function reference
def action1(self):
# Does something
def action2(self):
# Does something
Edit: I added in an example of how to pass in the game_map object you mentioned in the comments. You can then use self.game_map wherever in your action functions which will be the same object your game controller is updating as long as you are modifying that map each time instead of creating a new one.

How come I cannot access an instance of a class defined globally inside another class?

I'm trying to use the ui object within the Pawn class and I define and initiate ui outside everything, so it is global right? I've looked on here for questions relating to using variables outside classes but they seem to all refer to .self which I used in the UI class when initiating the Chess object.
The # represent bits of code I've cut out to help readability.
class UI:
def ___init__(self):
self.chess = Chess()
# Calls main menu function which goes to a new game option
def newGame(self):
self.chess.startGame()
while len(self.chess.pieces[0] == 2): # Which is index for Kings
# Game, which is where *move* is called
class Chess:
def startGame(self):
self.chessBoard = Grid(9,9)
self.pieces = []
self.pawns = []
for i in range(8):
self.pawns.append(PawnObject(x,y,side))
self.pieces.append(self.pawns)
class Grid:
# Init method
def cellOccupied(self,x,y):
# This function checks if a place is empty
# If empty, return false else, true
class Object:
# Sets x, y, width, height
class ChessPiece:
# Child of Object sets side (black or white)
class PawnObject:
def move(self,newX,newY):
if ui.chess.chessBoard.cellOccupied(newX,newY) == False:
# Move
# More code
ui = UI()
Traceback: https://gyazo.com/33e5b37b5290ff88433b29874c117ad7
Am I doing something blindingly wrong? I think the way I've programmed this all is very inefficient as I am still learning so is this a result of that? Thank you.
The problem is that this cascading series of events all happens inside the initialisation function for UI; one class calls the next, all before the original __init__ has had a chance to return. This means that the line that did that initialisation has not completed, so the ui variable does not exist yet.
You should try and move some of this out of that cascade. In particular, I can't see why the pawns should move as a result of initialising the UI class; that doesn't seem to make sense at all.
You should also consider why you need ui to be a global variable; seems more likely that it should be an attribute of one of the classes, perhaps Grid, and the pawn can reference it via that instance.
You're probably using ui in the body of a class, which is executed the moment the interpreter sees the class (and therefore before ui exists). You can only use it inside methods or functions since those are only executed when they're called. In other words:
class UI:
def open(self):
pass
class Chess:
ui.open() # <--- this causes an error because it happens before the last line does
def startGame(self):
ui.open() # <--- this is OK
ui = UI()
Also, I think your message indicates that you wrote global ui somewhere which is only necessary if you're planning on assigning a new value to ui, i.e. ui = something. If you just want to use it, e.g. ui.open(), you don't need the global declaration. But removing it won't solve your problem.
You also need to correct
chessBoard = Grid(9,9)
to
self.chessBoard = Grid(9,9)
to make chessBoard an attribute of Chess, which you need to be able to say ui.chess.chessBoard.

How to prevent properties from being shared between widgets?

I'm working on a game using Kivy where the player can press a key to shoot a bomb. Here's a simplified function:
def shoot(self, world):
# self is the player widget
bomb = Bomb(pos=self.pos)
world.add_entity(bomb)
The funny thing, when the player shoots, the bomb keeps moving with him wherever he goes.
After debugging I realized this is because both the player and the bomb share the velocity property.
The Player class and Bomb class both inherit Entity, which defined velocity = ObjectProperty(Vector(0, 0)) at class level. Debugging at runtime shows the two objects reference the exact same object.
The thing is, Kivy properties should actually create separate attributes on each instance. So why is this unwanted shared state, and how can I fix this?
The solution
Thanks to a comment by #Tshirtman to the answer on this question, I have found the problem and the solution.
Turns out Kivy properties have a gotcha, where the initial value set when instantiating them will be shared by all instances of the class.
To clarify:
# this implementation results in the property shared by all instances
class MyWidget(Widget):
my_field = ObjectProperty(MyObject())
w1 = MyWidget()
w2 = MyWidget()
w1.my_field is w2.my_field # True
As you can see, my_field is actually shared between the two instances.
To avoid this behavior, make sure to set the attribute on the particular object, in addition to setting the class-level property.
For example:
class MyWidget(Widget):
my_field = ObjectProperty()
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.my_field = MyObject() # explicitly set the value on the object
w1 = MyWidget()
w2 = MyWidget()
w1.my_field is w2.my_field # False
When explicitly setting the value for the field, the field is not shared between instances. Hope this helps someone.
Kivy's widgets would not do this automatically, probably you've written code that inadvertently causes it. Post a runnable example demonstrating the problem if you want help to track it down.

How to call another Object's Method from the current Object's Method in Python

I'm trying to simulate cars moving along a road in a graph-like fashion. Each Road object has a source and destination. When a car reaches the end of the road, I want the road to send it to the beginning of the next road. My code looks as follows for the Road class:
from collections import deque
class Road:
length = 10
def __init__(self, src, dst):
self.src = src
self.dst = dst
self.actualRoad = deque([0]*self.length,10)
Road.roadCount += 1
def enterRoad(self, car):
if self.actualRoad[0] == 0:
self.actualRoad.appendleft(car)
else:
return False
def iterate(self):
if self.actualRoad[-1] == 0:
self.actualRoad.appendleft(0)
else:
dst.enterRoad(actualRoad[-1]) #this is where I want to send the car in the last part of the road to the destination road!
def printRoad(self):
print self.actualRoad
testRoad = Road(1,2)
testRoad.enterRoad("car1")
testRoad.iterate()
In the code above, the problem is at the else part of the method iterate(): How do I call another Object's method from the current Object's Method? Both methods are in the same class.
It seems to me that you are confusing the difference between class and object.
A class is the piece of code where you model an object by specifying the attributes that compose its and the methods that defines its behaviors. In this case, the Road class.
In the other hand, an object is nothing else but an instance of the class that defines it. Therefore it has a state which is defined by the values of it attributes. Again, in this case, the testRoad is variable that stores an object of the Road class.
In one word, while the class is an abstract model, the object is a concrete instance with a well defined state.
So then when you say that you want:
call another Object's method from the current Object's Method
what you are actually wanting is to define in your class a method that allows you to call another method from an object of the same class.
Then, to do so, the class method needs to receive as parameter the object from which you want to invoke whatever method you want to invoke:
def iterate(self, destination_road):
if self.actualRoad[-1] == 0:
self.actualRoad.appendleft(0)
else:
destination_road.enterRoad(actualRoad[-1])
You must give the other Object as a parameter to iterate:
def iterate(self, other):
...
And to call a method from that object:
other.someMethod(...)

Categories