Python shallow/deep copy error - python

No, this is not a duplicate.
I have the following classes:
-Board
-Player
When Player is instantiated, it creates a new board object for that player.
I have a function which switches the current and latent players:
from copy import deepcopy
switch_players(self):
temp_val = deepcopy(self.current_player)
# self.current_player = None
self.current_player = deepcopy(self.latent_player)
# self.latent_player = None
self.latent_player = deepcopy(temp_val)
temp_val = None
This works fine for the players' names when I call self.current_player.name, but when I use self.current_player.board.display, or write to it, it references the same board for both players.
I even added code to create a deepcopy() of the board to switch like this, but I'm still experiencing the same issue. Is this a fundamentally wrong way to do something like this? Or is this approach correct, and I should look elsewhere in my code? (I have another way of doing this, but I'm not understanding the theory behind why this current approach doesn't work.)

It works so, because name is string, board is class instance which will be copied, but it is one level deeper, so it will not be copied with deepcopy. Deepcopy is not meant recursively.

Related

Python True Copy Within a Class

For some reason, I have a list that keeps getting modified despite being explicitly a deep copy. It appears to be unmodified as it goes through the loop, but it suddenly is modified once it exists? I must be missing something that pertains to Python's rules and logic, but I can't figure it out for the life of me.
def all_possible_states(self):
#creates many variations of a board from the available set of moves
to_return = [] #list of possible board states to return
#print(self.availible_moves)
list_to_copy = copy.deepcopy(self.availible_moves)
for item in self.availible_moves:
print('loop')
#append possible board state to list. set of availible moves for that board is one less. Done by removing item from that move list
to_return.append(board(self.player, copy.deepcopy(self.board.copy()), list_to_copy, self.plays, self.score, copy.deepcopy(item)))
#print(item)
print( self.avalible_moves) #shows the total set of moves. This is unmodified whenever it prints
print(list_to_copy)#deep copy of the original list. This is unmodified when it prints
print(to_return[len(to_return) - 1].availible_moves) #List of moves left availible for the board, this slowly shrinks for some reason each loop
print(self.availible_moves) #this is the original list, but it's not basically been cut all the way down for some reason
return to_return
Notice, the local variable list_to_copy is the deepcopy, not self.availible_moves. You are saving a deepcopy of self.availible_moves and it is being stored in the list_to_copy variable you defined. list_to_copy never changes as expected from a deepcopy. I'm not exactly sure what you are trying to do, but if you want, at the end you can reset self.availible_moves to be equal to list_to_copy and then it will be as if it never changed.
EDIT:
Actually, I think you have a spelling mistake, notice that you print(self.avalible_moves) and you are saying its not changing, when really what is changing is self.availible_moves. Notice the extra letter i in the first expression. This definitely is one of your problems.

(Python) adding a list to another without the brackets

I'm currently making my first project in Python - tic tac toe as i thought it would be a good place to start and I've finally hit a road block.
I've currently got the board setup as a list - Board = [1,2,3...,9] and to reset, Board's contents is deleted and re-entered by a separate list however it also adds the brackets.
Is there a way to grab the contents of a separate list without also grabbing the brackets?
if newGame == 'Y' or 'y':
del Board[:]
Board.append(startBoard)
print Board #for testing
else:
sys.exit('game over')
What I'm getting is looking like this:
[[1,2,3,4,5,6,7,8,9]]
Use extend.
Board.extend(startBoard)
Alternatively you can do:
Board = [el for el in startBoard]
and omit the del Board[:] althogether.
Alternatively you can use the copy module:
Board = copy.deepcopy(startBoard)
For me the best will be this though:
Board = [i+1 for i in xrange(9)]
Or the simpler:
Board = range(1, 10) # python 2
or
Board = list(range(1, 10)) # python 3
As zehnpaard suggested in the comments.
Also you can do what Erik Allik has proposed in his answer.
if you want to clear out and modify the contents of a list in-place (as opposed to creating a new fresh list and assigning it back to the variable that holds your old list):
board[:] = startBoard
which, if nothing that needs to see the latest board contents points to the old list, is functionally equivalent to:
board = startBoard
you can get an idea of what it means to modify something in-place vs create a fresh copy/instance if you play around a little bit with assigning the same contents to multiple variables without making copies in the process.
Also, note that since startBoard in your case is a constant, you want to make sure nothing ever modifies it, so the 2nd example is really a very bad idea. There's also however a 3rd:
board = startBoard[:]
which is actually by far the best of all three. What it does is make a copy of startBoard and assign it to the variable board without ever modifying the old list board was pointing to. Also, because you're using a copy of startBoard, you can be sure your start board is always the same in the future.
Note that these aspects arise only in programming languages or styles where mutable datatypes are used; in Functional Programming, almost nothing ever gets modified so you can completely become ignorant of worrying about causing accidental adverse side-effect by touching something someone else is relying on.
Furthermore: note that copying a list can be done in a multitude of ways in Python; from a high level point of view which I'm currently speaking out for there is little difference though, so copy.copy(startBoard) is the same as [x for x in startBoard) is the same as startBoard[:] etc.
See also the PEP8 which provides you with useful coding conventions.
Try this:
for x in startBoard:
Board.append(x)

League table in Python - it doesn't insert teams into the list

I need to make a league table for a project. There has to be 3 files,2 files consist of 1 class and the last file is for running a program. I have done all of the parts but when I call a method to add a team, the program adds the name but it does not insert it into the list of teams(which should do). When I try to display the items in the list, the program displays an error message instead of showing the actual team.
How can I fix it?Any help would be appreciated. :)
A few things here:
When I try to display the items in the list, the program displays: team.Team object at 0x000000000332A978 insted of showing the actual team.
The default display for a user class is something like <team.Team object at 0x000000000332A978>. If you want it to display something different, you have to tell Python what you want to display. There are two separate functions for this: __repr__ and __str__. The idea is that the first is a representation for the programmer, the second for the user. If you don't need two different representations, just define __repr__ and it'll use that whenever it needs __str__.
So, a really simple way to fix this is to add this to the Team class:
def __repr__(self):
return 'Team("{}")'.format(self._name)
Now, if you call league.addTeam('Dodgers'), then print(l._table), you'll get [Team("Dodgers")] instead of [<team.Team object at 0x000000000332A978>].
Meanwhile, these two methods are probably not what you want:
def removeTeam(self,team):
self._table.remove(team)
def returnPosition(self,team):
return self._table.index(team)
These will remove or find a team given the Team object—not the name, or even a new Team created from the name, but a reference to the exact same object stored in the _table. This is not all that useful, and you seem to want to call them with just names.
There are two ways to fix this: You could change Team so that it compares by name instead of by object identity, by adding this method to the class:
def __eq__(self, other):
return self._name == other._name
What this means is that if you say Team('Giants') == Team('Giants'), it will now be true instead of False. Even if the first team is in a different league, and has a different W-L record, and so on (e.g., like the baseball "Giants" from San Francisco vs. the football "Giants" from New York), as far as Python is concerned, they're now the same team. Of course if that's not what you want, you can write any other __eq__ function that seems more appropriate.
Anyway, if you do this, the index and remove functions will now be able to find any Team with the same name, instead of just the exact same team, so:
def removeTeam(self,team_name):
self._table.remove(Team(team_name))
def returnPosition(self,team_name):
return self._table.index(Team(team_name))
If you go this way, you might want to consider defining all of the comparison methods, so you can, e.g., sort a list of teams, and they sort by name.
Or you could change these methods so they don't work based on equality, e.g., by redefining them like this:
def removeTeam(self,team_name):
self._table = [team for team in self._table if team._name != team_name]
def returnPosition(self,team_name):
return [team._name for team in self._table].index(team_name)
To understand how these work, if you're not used to reading list comprehensions, turn each one back into the equivalent loop:
self._table = [team for team in self._table if team._name != team_name]
temp = []
for team in self._table:
if team._name != team_name:
temp.append(team)
self._table = temp
If you step through this, temp ends up with a list of every team in the table, except the one you wanted to remove, and then you replace the old self._table with the new filtered one. (Another way to write the same idea is with filter, if you know that function.)
It's usually better to create a new filtered list than to modify a list in-place. Sometimes there are performance reasons not do this, and sometimes it ends up being very complex and hard to understand, but it's usually both faster and simpler to reason about. Also, modifying lists in place leads to problems like this:
for i, value in enumerate(mylist):
if value == value_to_remove:
del mylist[i]
Play with this for a while, and you'll see that it doesn't actually work. Understanding why is a bit complicated, and you probably don't want to learn that until later. The usual trick to solve the problem is to iterate over a copy of the list… but once you're doing that, you've now got the worst of filtering and the worst of deleting-in-place at the same time.
The second function may be a little too clever, but let's look at it:
def returnPosition(self,team_name):
return [team._name for team in self._table].index(team_name)
First, I'm creating a list like the original one, but it's a list of just the names instead of the team objects. Again, let's decompose the list comprehension:
temp = []
for team in self._table:
temp.append(team._name)
Or try to translate it into English: This is a list of the team name of every team in the table.
Now, because this is a list of team names, I can use index(team_name) and it will find it. And, because the two lists have the same shape, I know that this is the right index to use in the original team list as well.
A much simpler solution would be to change _tables from a list of Teams into a dict mapping names to Teams. This is probably the most Pythonic solution—it looks a lot simpler than writing list comprehensions to do simple operations. (It's also probably the most efficient, but that's hardly relevant unless you have some truly gigantic leagues.) And then you don't even need returnPosition for anything. To do that:
def __init__(self):
self._table={}
def addTeam(self,name):
self._table[name]=Team(name)
def removeTeam(self,team_name):
del self._table[team_name]
def returnPosition(self,team_name):
return team_name
def updateLeague(self,team1_name1,team_name2,score1,score2):
if score1>score2:
self._table[team_name1].win()
self._table[team_name2].loss()
elif score1==score2:
self._table[team_name1].draw()
self._table[team_name2].draw()
elif score1<score2:
self._table[team_name1].loss()
self._table[team_name2].win()
Note that I've defined returnPosition to just return the team name itself as the position. If you think about it, dict keys are used exactly the same way as list indices, so this means any code someone wrote for the "old" API that required returnPosition will still work with the "new" API. (I probably wouldn't try to sell this to a teacher who assigned a problem that required us to use returnPosition, but for a real-life library where I wanted to make it easier for my 1.3 users to migrate to 2.0, I probably would.)
This only requires a few other changes. In displayList and saveList, you iterate over self._table.values() rather than self._table; in loadList, you change self._table.append(team) to self._table[a] = team. Speaking of loadList: You might want to consider renaming those local variables from a, b, c, and d to name, wins, losses, and draws.
A few other comments:
As kreativitea says in the comments, you should not create "private" variables and then add do-nothing accessor methods in Python. It's just more boilerplate that hides the real code, and one more thing you can get wrong with a silly typo that you'll spend hours debugging one day. Just have members named name, wins, losses, etc., and access them directly. (If someone told you that this is bad style because it doesn't let you replace the implementation in the future without changing the interface, that's only true in Java and C++, not in Python. If you ever need to replace the implementation, just read up on #property.)
You don't need print("""""")—and it's very easy to accidentally miscount the number of " characters. (Especially since some IDEs will actually be confused by this and think the multi-line string never ends.) Just do print().
You've got the same ending condition both in the while loop (while x!="q":) and in an internal break. You don't need it in both places. Either change it to while True:, or get rid of the break (just make options("q") do print("Goodbye"), so you don't need to special-case it at all inside the loop).
Whenever you have a long chain of elif statements, think about whether you can turn it into a dict of short functions. I'm not sure it's a good idea in this case, but it's always worth thinking about and making the explicit decision.
The last idea would look something like this:
def addTeam():
name=input("Enter the name of the team:")
l.addTeam(name)
def removeTeam():
teamToRemove=input("Enter the name of the team you want to remove:")
l.removeTeam(teamToRemove)
def recordGame():
team1=input("What is the name of the team?")
ans1=int(input("Enter the number of goals for the first team:"))
team2=input("What is the name of the team?")
ans2=int(input("Enter the number of goals for the second time:"))
l.updateLeague(team1,team2,ans1,ans2)
optionsdict = {
"a": addTeam,
"d": l.displayList,
"s": l.saveList,
"l": l.loadList,
"r": removeTeam,
"rec": recordGame,
}
def options(x):
func = optionsdict.get(x)
if func:
func()
As I said, I'm not sure it's actually clearer in this case, but it's worth considering.

How to copy an object without referencing to it?

I'm not sure if my title is correct for what I'm looking for, but I think that the referencing is the problem.
I have a Reader object through which I can loop:
msrun = pymzml.run.Reader(mzmlFile)
for feature in msrun:
print feature['id']
With this code I get the id's, starting at 1, of all the features in msrun. However, I need to loop through the code first and get all the keys that I want and put them in a list, like this:
def getKeys(msrun, excludeList):
spectrumKeys = []
done = False
for spectrum in msrun:
if done:
break
if spectrum['ms level'] == 2:
for key in spectrum:
if key not in excludeList and not key.startswith('MS:'):
done = True
spectrumKeys.append(key)
spectrumKeys.extend(spectrum['precursors'][0].keys())
precursorKeys = spectrum['precursors'][0].keys()
break
return spectrumKeys, precursorKeys
However, if I would run this code:
msrun = pymzml.run.Reader(mzmlFile)
specKeys, precursKeys = getKeys(msrun, ['title','name'])
for feature in msrun:
print feature['id']
it starts of at the id that hasn't been in the loop in getKeys() (it starts at 11 instead of 1). So I guess pymzml.run.Reader() works like a generator object. So I tried copying the object. First I tried
copyMsrun = msrun
specKeys, precursKeys = getKeys(copyMsrun, ['title','name'])
But this gives the same problem, if I understood correctly because doing copyMsrun = msrun makes them point to the same thing.
Then I tried
import copy
copyMsrun = copy.copy(msrun)
But I still had the same problem. I used copy.copy instead of copy.deepcopy because I don't think that the Reader objects contains other objects, and when I try deepcopy I get
TypeError: object.__new__(generator) is not safe, use generator.__new__().
So how do I copy an object so that looping through one doesn't affect the other? Should I just do
msrun = pymzml.run.Reader(mzmlFile)
copyMsrun = pymzml.run.Reader(mzmlFile)
?
Edit:
On Ade YU's comment, I tried that too but when I do
spectrumList = []
for spectrum in msrun:
print spectrum['id']
spectrumList.append(spectrum)
for spectrum in spectrumList:
print spectrum['id']
The first print gives me 1-10, but the second print give me ten times 10
From the publication of pymzML and the documentation, it is clear that this "pathologically design" is done on purpose. Initializing thousands of spectrum objects will create a huge computational overhead, memory and cpu cycle wise that are simply not needed. Normally, parsing large sets of mzML naturally calls for analyze-while-parsing approach rather then collecting everything one needs to analyze later.
Having said this, pymzML still offers the function to "deep copy" the spectrum simply by calling spectrum.deRef(). The advantage by using this function is that all unnecessary data will be stripped prior copying, hence offering smaller objects. pymzML deRef
run = pymzml.run.Reader(file_to_read, MS1_Precision = 5e-6, MSn_Precision = 20e-6)
for spec in run:
tmp = spec.deRef()
Hope that helps.
It looks like you're dealing with a pathologically designed class. There are some serious flaws in the library you are using, especially the part where the iterator yields the same object over and over again.
You'll probably need to copy the output of the iterator, like this:
objs = [copy.deepcopy(obj) for obj in pymzml.run.Reader(mzmlFile)]
for obj in objs:
# do something
for obj in objs:
# do something
If that doesn't work, you need to find whoever wrote the library and confiscate their computer.
Try itertools.tee, which gives you independent iterators. If this doesn't work, you are probably in trouble, because the objects yielded by your generator depend on some external state, (id = number of objects yielded so far?), and there is no way to automatically help in that situation. deepcopy is your best bet, but if that doesn't work, you'll have to write your own class that captures alle the info from the spectrum objects.
spectrumList = []
for spectrum in msrun:
spectrumList.append(MySpectrum(spectrum))
or the shorter variant
spectrums = list(map(MySpectrum(msrun)))
You'll need something like
class MySpectrum:
def __init__(self, spectrum):
self.id = spectrum.id
...
Use the deepcopy module to assign them without pointing to the same object
from copy import deepcopy
myq=deepcopy(transq)

Python: iterating through a list of objects within a list of objects

I've made two classes called House and Window. I then made a list containing four Houses. Each instance of House has a list of Windows. I'm trying to iterate over the windows in each house and print it's ID. However, I seem to get some odd results :S I'd greatly appreciate any help.
#!/usr/bin/env python
# Minimal house class
class House:
ID = ""
window_list = []
# Minimal window class
class Window:
ID = ""
# List of houses
house_list = []
# Number of windows to build into each of the four houses
windows_per_house = [1, 3, 2, 1]
# Build the houses
for new_house in range(0, len(windows_per_house)):
# Append the new house to the house list
house_list.append(House())
# Give the new house an ID
house_list[new_house].ID = str(new_house)
# For each new house build some windows
for new_window in range(0, windows_per_house[new_house]):
# Append window to house's window list
house_list[new_house].window_list.append(Window())
# Give the window an ID
house_list[new_house].window_list[new_window].ID = str(new_window)
#Iterate through the windows of each house, printing house and window IDs.
for house in house_list:
print "House: " + house.ID
for window in house.window_list:
print " Window: " + window.ID
####################
# Desired output:
#
# House: 0
# Window: 0
# House: 1
# Window: 0
# Window: 1
# Window: 2
# House: 2
# Window: 0
# Window: 1
# House: 3
# Window: 0
####################
Currently you are using class attributes instead of instance attributes. Try changing your class definitions to the following:
class House:
def __init__(self):
self.ID = ""
self.window_list = []
class Window:
def __init__(self):
self.ID = ""
The way your code is now all instances of House are sharing the same window_list.
Here's the updated code.
# Minimal house class
class House:
def __init__(self, id):
self.ID = id
self.window_list = []
# Minimal window class
class Window:
ID = ""
# List of houses
house_list = []
# Number of windows to build into each of the for houses
windows_per_house = [1, 3, 2, 1]
# Build the houses
for new_house in range(len(windows_per_house)):
# Append the new house to the house list
house_list.append(House(str(new_house)))
# For each new house build some windows
for new_window in range(windows_per_house[new_house]):
# Append window to house's window list
house_list[new_house].window_list.append(Window())
# Give the window an ID
house_list[new_house].window_list[new_window].ID = str(new_window)
#Iterate through the windows of each house, printing house and window IDs.
for house in house_list:
print "House: " + house.ID
for window in house.window_list:
print " Window: " + window.ID
The actual problem is that the window_list attribute is mutable, so when the different instances are using it, they end up sharing the same one. By moving window_list into __init__ each instance gets its own.
C++, Java, C# etc. have this really strange behaviour regarding instance variables, whereby data (members, or fields, depending on which culture you belong to) that's described within a class {} block belongs to instances, while functions (well, methods, but C++ programmers seem to hate that term and say "member functions" instead) described within the same block belong to the class itself. Strange, and confusing, when you actually think about it.
A lot of people don't think about it; they just accept it and move on. But it actually causes confusion for a lot of beginners, who assume that everything within the block belongs to the instances. This leads to bizarre (to experienced programmers) questions and concerns about the per-instance overhead of these methods, and trouble wrapping their heads around the whole "vtable" implementation concept. (Of course, it's mostly the teachers' collective fault for failing to explain that vtables are just one implementation, and for failing to make clear distinctions between classes and instances in the first place.)
Python doesn't have this confusion. Since in Python, functions (including methods) are objects, it would be bizarrely inconsistent for the compiler to make a distinction like that. So, what happens in Python is what you should intuitively expect: everything within the class indented block belongs to the class itself. And, yes, Python classes are themselves objects as well (which gives a place to put those class attributes), and you don't have to jump through standard library hoops to use them reflectively. (The absence of manifest typing is quite liberating here.)
So how, I hear you protest, do we actually add any data to the instances? Well, by default, Python doesn't restrict you from adding anything to any instance. It doesn't even require you to make different instances of the same class contain the same attributes. And it certainly doesn't pre-allocate a single block of memory to contain all the object's attributes. (It would only be able to contain references, anyway, given that Python is a pure reference-semantics language, with no C# style value types or Java style primitives.)
But obviously, it's a good idea to do things that way, so the usual convention is "add all the data at the time that the instance is constructed, and then don't add any more (or delete any) attributes".
"When it's constructed"? Python doesn't really have constructors in the C++/Java/C# sense, because this absence of "reserved space" means there's no real benefit to considering "initialization" as a separate task from ordinary assignment - except of course the benefit of initialization being something that automatically happens to a new object.
So, in Python, our closest equivalent is the magic __init__ method that is automatically called upon newly-created instances of the class. (There is another magic method called __new__, which behaves more like a constructor, in the sense that it's responsible for the actual creation of the object. However, in nearly every case we just want to delegate to the base object __new__, which calls some built-in logic to basically give us a little pointer-ball that can serve as an object, and point it to a class definition. So there's no real point in worrying about __new__ in almost every case. It's really more analogous to overloading the operator new for a class in C++.) In the body of this method (there are no C++-style initialization lists, because there is no pre-reserved data to initialize), we set initial values for attributes (and possibly do other work), based on the parameters we're given.
Now, if we want to be a little bit neater about things, or efficiency is a real concern, there is another trick up our sleeves: we can use the magic __slots__ attribute of the class to specify class attribute names. This is a list of strings, nothing fancy. However, this still doesn't pre-initialize anything; an instance doesn't have an attribute until you assign it. This just prevents you from adding attributes with other names. You can even still delete attributes from an object whose class has specified __slots__. All that happens is that the instances are given a different internal structure, to optimize memory usage and attribute lookup.
The __slots__ usage requires that we derive from the built-in object type, which we should do anyway (although we aren't required in Python 2.x, this is intended only for backwards-compatibility purposes).
Ok, so now we can make the code work. But how do we make it right for Python?
First off, just as with any other language, constantly commenting to explain already-self-explanatory things is a bad idea. It distracts the user, and doesn't really help you as a learner of the language, either. You're supposed to know what a class definition looks like, and if you need a comment to tell you that a class definition is a class definition, then reading the code comments isn't the kind of help you need.
With this whole "duck typing" thing, it's poor form to include data type names in variable (or attribute) names. You're probably protesting, "but how am I supposed to keep track of the type otherwise, without the manifest type declaration"? Don't. The code that uses your list of windows doesn't care that your list of windows is a list of windows. It just cares that it can iterate over the list of windows, and thus obtain values that can be used in certain ways that are associated with windows. That's how duck typing works: stop thinking about what the object is, and worry about what it can do.
You'll notice in the code below that I put the string conversion code into the House and Window constructors themselves. This serves as a primitive form of type-checking, and also makes sure that we can't forget to do the conversion. If someone tries to create a House with an ID that can't even be converted to a string, then it will raise an exception. Easier to ask for forgiveness than permission, after all. (Note that you actually have to go out of your way a bit in Python to create
As for the actual iteration... in Python, we iterate by actually iterating over the objects in a container. Java and C# have this concept as well, and you can get at it with the C++ standard library too (although a lot of people don't bother). We don't iterate over indices, because it's a useless and distracting indirection. We don't need to number our "windows_per_house" values in order to use them; we just need to look at each value in turn.
How about the ID numbers, I hear you ask? Simple. Python provides us with a function called 'enumerate', which gives us (index, element) pairs given an input sequence of elements). It's clean, it lets us be explicit about our need for the index to solve the problem (and the purpose of the index), and it's a built-in that doesn't need to be interpreted like the rest of the Python code, so it doesn't incur all that much overhead. (When memory is a concern, it's possible to use a lazy-evaluation version instead.)
But even then, iterating to create each house, and then manually appending each one to an initially-empty list, is too low-level. Python knows how to construct a list of values; we don't need to tell it how. (And as a bonus, we typically get better performance by letting it do that part itself, since the actual looping logic can now be done internally, in native C.) We instead describe what we want in the list, with a list comprehension. We don't have to walk through the steps of "take each window-count in turn, make the corresponding house, and add it to the list", because we can say "a list of houses with the corresponding window-count for each window-count in this input list" directly. That's arguably clunkier in English, but much cleaner in a programming language like Python, because you can skip a bunch of the little words, and you don't have to expend effort to describe the initial list, or the act of appending the finished houses to the list. You don't describe the process at all, just the result. Made-to-order.
Finally, as a general programming concept, it makes sense, whenever possible, to delay the construction of an object until we have everything ready that's needed for that object's existence. "Two-phase construction" is ugly. So we make the windows for a house first, and then the house (using those windows). With list comprehensions, this is simple: we just nest the list comprehensions.
class House(object):
__slots__ = ['ID', 'windows']
def __init__(self, id, windows):
self.ID = str(id)
self.windows = windows
class Window(object):
__slots__ = ['ID']
def __init__(self, id):
self.ID = str(id)
windows_per_house = [1, 3, 2, 1]
# Build the houses.
houses = [
House(house_id, [Window(window_id) for window_id in range(window_count)])
for house_id, window_count in enumerate(windows_per_house)
]
# See how elegant the list comprehensions are?
# If you didn't quite follow the logic there, please try **not**
# to imagine the implicitly-defined process as you trace through it.
# (Pink elephants, I know, I know.) Just understand what is described.
# And now we can iterate and print just as before.
for house in houses:
print "House: " + house.ID
for window in house.windows:
print " Window: " + window.ID
Apart from some indentation errors, you're assigning the IDs and window_lists to the class and not the instances.
You want something like
class House():
def __init__(self, ID):
self.ID = ID
self.window_list = []
etc.
Then, you can do house_list.append(House(str(newHouse))) and so on.

Categories