Python get parent class - python

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.

Related

Change object's class

In a chess game, I define p1 = Pawn().
I'd like to be able to promote it:
def promote(self, piece):
pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'
try:
self = piece(pos, color=self.color, num='s')
except NameError:
print(f'Cannot promote {self.pieceid} to {piece}')
However, feeding in Queen or Bishop doesn't actually change the object from being a Pawn. I think it's because of scoping:
class Chesspiece:
...
class Pawn(ChessPiece):
def promote()
...
class Queen(Chesspiece):
...
How can I change the class of an object?
Reassigning any bare name (including self) rebinds the name; whatever was in there before is thrown away, and the name now points to something unrelated.
So when you do:
self = piece(pos, color=self.color, num='s')
you just lose access to the "real" self (the object the method was called on), you don't change the object it was called on.
The only way to do what you want is kinda hacky; to reassign __class__ on self (and change any other attributes needed to make it a valid instance of the new class).
So in this case, you might be able to just do:
self.__class__ = piece
and if attribute values might need to be recreated/revalidated, or piece might be a factory function or weirdo class whose __new__ doesn't necessarily return the type it was called on, you'd create a new piece as a template, then copy from it, like so:
# Make a new piece to copy from
newpiece = piece(pos, color=self.color, num='s')
vars(self).clear() # Clear out existing attributes
vars(self).update(vars(newpiece)) # Copy attributes from new piece
self.__class__ = newpiece.__class__ # Using newpiece.__class__ ensures it's the resulting class
# in case of factory functions, weird __new__, etc.
Note: This is usually not what you want to do. As Rocky Li mentions in the comments, the usual approach is to replace the object in whatever structure holds it, rather than updating the class in place on an existing object, e.g. if you have variable named board which is a 2D list of lists, you'd just do:
a_piece = ... # Piece to replace
new_piece_type = ... # Type of piece it's being replaced with
pos = f'{Config.tile_convert(a_piece.x)}{Config.tile_convert(a_piece.y, True)}'
# Replace entire object (and local alias name) at known coordinates with new object
board[a_piece.x][a_piece.y] = a_piece = new_piece_type(pos, color=a_piece.color, num='s')

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.

array in the class does not get initialized

I am new to python and trying to learn.
Problem
I am stuck at a place where I initialize and object of a class in the loop. During this initialization I expect that I get a brand new object with all its attribute reset. But that doesn't happen when for the array list the class has.
Example below:
Class MyClass:
id=""
aList=[]
Class SecondClass: # ignore its content
pid=""
anObj=MyClass()
sc1=SecondClass()
anobj.append(sc1)
print str(len(anObj.aList) # gives the output 1
anObj=MyClass() # reinitalizing the object, so I expect on the lines of java that I get an empty class
print str(len(anObj.aList) # gives the output 1, why I just reinitialized the object "anObj"
What I want is after I do anObj=MyClass() all the attributes should be reset. Like java. It seems like anObj (array ) is treated as static variable of class (using Java language)
Problem at deeper depth
I don't want to do anObj.aList=[] explicitly because my issue is some thing like in the below code
aCollection=[]
for x in (0,3):
anObj=MyClass()
sc=getSecondClassObjectWithDifferentValues()
anobj.append(sc)
aCollection.append(anOb)
I am putting anObj in aCollection, eventually I would like to access them in the state I put it.
Thanks in advance for the help
You are confusing static properties with instance property. You should be doing this instead:
Class MyClass:
def __init__(self):
self.id = ""
self.aList = []
The fundamental difference is that, in your implementation, the property aList will be the same for all instances of MyClass. This is why we call them static, because they do not change from instance to instance. In contrast, an instance variable defined as above will be unique for each new instance you create.
This is a super common misunderstanding with python ... effectively, MyClass.aList is a "static" member. The key to understanding this is to understand how python looks up attributes on an object.
First, python look at the instance for the attribute. If it isn't there, then python moves up a level (to the class) and looks for the attribute there. If it isn't on the class, it'll look at the base classes (in the "Method Resolution Order").
So, this (hopefully) explains the problem. you create an instance of MyClass and get a reference to it's list:
c = MyClass()
lst = c.aList
Now, note, c.aList is MyClass.aList because c doesn't have an aList attribute of it's own.
print(lst is MyClass.aList) # True
So, how do we resolve this? The typical fix for this is to bind the aList attribute to the instance at initialization time:
class MyClass(object):
def __init__(self):
self.aList = []
Now, MyClass doesn't have an aList member, but all of it's instances will (and their members will all be distinct).

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

Python new list not created upon object creation [duplicate]

This question already has answers here:
Weird list behavior in class
(4 answers)
Closed 9 years ago.
I'm doing some practicing with OOP in python and I've run into an issue that my non-computer scientist mind cannot comprehend. I'm sure it's just due to my inexperience with OO but I can't seem to find an answer for it anywhere.
So I've got three classes. A class called tester, which should contain a unique object called group, which should contain a list of objects called atom. My issue is that whenever I create multiple groups they all seem to have the same list object. So whenever I append an atom to the list it gets appended to all the group's lists. My code is:
count = 0
testers = []
class atom:
def __init__(self):
pass
class group:
myList = list()
def __init__(self):
pass
def createAtom(self):
self.myList.append(atom())
class tester:
def __init__(self):
self.myGroup = group()
for k in range(4):
testers.append(tester())
print testers[k].myGroup
for t in testers:
t.myGroup.createAtom()
print t.myGroup.myList
I would expect this to create a new list for each group and that this would add a single atom to each group. This instead creates an output as follows.
<__main__.group instance at 0x02C2E058>
<__main__.group instance at 0x02C2E0A8>
<__main__.group instance at 0x02C2E0F8>
<__main__.group instance at 0x02C2E148>
[<__main__.atom instance at 0x02C2E170>]
[<__main__.atom instance at 0x02C2E170>, <__main__.atom instance at 0x02C2E198>]
[<__main__.atom instance at 0x02C2E170>, <__main__.atom instance at 0x02C2E198>, <__main__.atom instance at 0x02C2E1C0>]
[<__main__.atom instance at 0x02C2E170>, <__main__.atom instance at 0x02C2E198>, <__main__.atom instance at 0x02C2E1C0>, <__main__.atom instance at 0x02C2E1E8>]
A single list gets all four atoms. I apologize for my likely poor code. If it's of any help, I'm using python portable 2.7.5.1. Any insight into this would be greatly appreciated.
Thanks
Your list is a class attribute, shared amongst all instances:
class group:
myList = [] # class attribute
def __init__(self):
pass
Instead, make it an instance attribute, separate for each instance of the class:
class group:
def __init__(self):
self.myList = [] # instance attribute
Note that I have replaced list() with [], per thefourtheye's comment. It is bad practice to shadow built-ins (e.g. having your own list or other object named list), but this avoids side effects if the rule gets broken.
You've made group.myList a class attribute, shared by all instances.
class group:
#myList = list() # <--- this defines a 'class' attribute
# which is shared by all instances of 'group'
def __init__(self):
self.myList = list() # <--- do this instead to create an instance attribute
def createAtom(self):
self.myList.append(atom())
Move the mylist = list() in class group into the __init__ of class group.
Doing so would make group create a new list every time a new group instance is created. Otherwise, all you've done is create a class-level variable (not instance-level), which will be shared among all instances of the same class.
Think of class variables as sort of a "hive mind" (think of The Borg from Star Trek) structure for all instances of that class. Any changes made to a class variable will be visible to all objects of that type.
On the other hand, if you were to create an instance variable (a variable initialized in __init__), then each instance would have its own value for that variable. Thus any changes that one instance makes to its variable will be invisible to other instances of the same type

Categories