Python: Dictionary of Dictionaries

Started by
18 comments, last by Zahlman 17 years, 9 months ago
You like doing things the hard way, don't you? :) You don't need a regular expression for what you're doing - just use cmd.split or something like that.
Advertisement
Well, Im fairly new to Python...so I guess my code implementation is pretty inefficient :D I will give cmd.split a try, thanks!

But, I still havnt been able to figure out why the program cant find the correct key and how to store the last room description.

Anyone have any pointers?
Ok, Ive figured out how to access my dictionaries. But, it seems to be giving me a memory location of the key instead of the value (name and description of the room/player/item).

This is the output I get when I use: print room["2"]

<Adventure> create room 2 kitchen stuff
ID: 2, name: kitchen, desc: stuff
<__main__.Room instance at 0xb7ee436c>


How would I get the value associated with the key value "2"?
Quote:
But, it seems to be giving me a memory location of the key instead of the value (name and description of the room/player/item).


room[2] returns an instance of the Room class because that IS the value. The class hasn't been designed to be printed so it's giving you a representation of the instance. The strings you want are called "members" or "member variables" and you access them like this...

instance.member

...so to print out your strings you would do...

print room[2].roomname
print room[2].roomdesc

But you could also define a special __str__ function if you want to code this representation into the class itself.
class Room:    # ...previous stuff here...    def __str__(self):        return self.roomname + '\n' + self.roomdesc


Then print room[2] would print the return value of __str__.
Ohhh I see! Thats great. Thanks for your help ^^

Learning something new everyday :D
Quote:Original post by Zahlman
Like Fruny said, Python dictionaries (and lists and tuples) don't care about what they contain: they are 'heterogenous containers'. So if you really wanted a dictionary of dictionaries, you could do that quite easily, because a dictionary is a kind of object, and any object can be a dictionary value.

That said, it sounds like your confusion has more to do with modelling the situation than the syntax of implementing that model :)

First off, when you talk about "IDs", this implies a separate object from the thing being ID'd. That's fine, but you probably only want a name in the ID, leaving the description in the thing being IDd.

We have four sorts of things:

- player
- player ID
- room
- room ID

Because the IDs are just names, we could just use strings for them. The player and room both aggregate some information and will have some behaviour, so it makes sense to define classes for them.

Now, a room might contain players, and similarly a player knows about the room that s/he's in. We don't actually need IDs to handle this, because what we'll do is make each room track a list of the contained players, and have each player remember the containing room directly. There's a bit of a trick here: because of how Python stores objects, you don't really distinguish between object members that are "contained" and object members that are "known about" in the syntax - it's a matter of semantics.

So, we do something like:

class Room:  def __init__(self):    self.players = [] # will be Players that are "contained"class Player:  def __init__(self, location):    self.location = location # will be a Room that is "known about" (containing)


Note that the Room won't *actually* "contain" Players, but instead points to them, in the same way that the Player points to its containing room. Hence the semantics. :)

Now, we just add the functionality to move a Player from one room to the next. We should also make sure that the Player's initial Room knows about the Player, so we'll adjust the constructor as well. We can make this more foolproof by setting an invalid location first, and then "moving" the player into the initial Room.

class Room:  def __init__(self):    self.players = []  def removePlayer(self, player):    # I don't remember if this throws an exception or returns -1 for not found    # So adjust as needed :)    index = self.players.find(player)    if (index != -1) : del self.players[index]  def addPlayer(self, player):    self.players.append(player)class Player:  def __init__(self, location):    self.location = None    self.moveTo(location)  def moveTo(self, location):    if self.location is not None:      # excuse myself from the current room, if any      self.location.removePlayer(self)      # and go to the new one.      location.addPlayer(self)      self.location = location



Zahlman, when I try to implement the code you gave, and when I try to move players around into a room, i get this error:

>>> AttributeError: 'str' object has no attribute 'addPlayer'


The code I have is this:
class Room:        def __init__(self, roomname, roomdesc):                self.roomname = roomname                self.roomdesc = roomdesc                self.players = []                       # players contained in this room        def removePlayer(self, player):                index = self.players.remove(player)     # removes player (using playerID) from the room (when player leaves)        def addPlayer(self, player):                    # adds player to the room (when player enters)                self.players.append(player)        def __str__(self):                return self.roomname + ": " + self.roomdesc  # prints out the room name and room descriptionclass Player:        def __init__(self, playername, playerdesc, location):                self.playername = playername                self.playerdesc = playerdesc                self.location = 0                               # default starting room of all players (roomID = 0)                self.moveTo(location)                           # location of player. ie what room he/she is in        def moveTo(self, location):                if location != 0:                        self.location.removePlayer(self)                # remove player from original room                        location.addPlayer(self)                        # add player to new room                        self.location = location                        # update player room location        def __str__(self):                return self.playername + ": " + self.playerdesc


Is the Player class not able to see the removePlayer and addPlayer functions in the Room class? Would I have to specify identical functions in my Player class to rectify this?
Looking at your code it should be saying AttributeError: 'int' object has no attribute... instead of 'str'. 'str' is the Python string type, meaning you somehow assigned a string to the member variable 'location', but looking at your code I only see you assigned it a number. You can put in a print self.location just before the line where the error is to see what the real value is. Anyway, neither numbers nor strings have the 'addPlayer' method you are trying to call. You want a Room instance. Maybe you want to use the number/string stored in 'location' to retrieve one...remember how?
Yes, you are correct that it is assigned an int type. Before the error, self.location contains the default value of 0. But, looking at my input command, I assign it a 3, yet this value is not passed. It also will not recognize the self.location.removePlayer and location.addPlayer functions?
Quote:Original post by fengx
Yes, you are correct that it is assigned an int type. Before the error, self.location contains the default value of 0.

Use None as the default value, not 0. Python has a built in null value; take advantage of it.

Quote:But, looking at my input command, I assign it a 3, yet this value is not passed. It also will not recognize the self.location.removePlayer and location.addPlayer functions?

Python is a late binding language, which means that the function calls are resolved at runtime based on the type of the actual value in the variable. If you store an int in location, then a call to location.addPlayer will fail because the int class doesn't have an addPlayer member. You need to store a Room object in location in order not to cause an exception.

Plus, your Player.__init__ should make the final parameter default like so:
class Player:        def __init__(self, playername, playerdesc, location = None):                self.playername = playername                self.playerdesc = playerdesc                self.location = location                self.moveTo(location)                           # location of player. ie what room he/she is in        def moveTo(self, location):                if location:                                    # fails on <None>                        self.location.removePlayer(self)        # remove player from original room                        location.addPlayer(self)                # add player to new room                        self.location = location                        # update player room location


Finally, you want to call it like so:
basement = Room()p = Player("Hero", "A valorous knight", basement)
Olu, Hollower, both of you are correct, but you missed the point - the if-check is supposed to be against self.location. You can't remove the player from the current location before the first assignment of a "real" location :) The code is to check "if I'm already in a Room, that Room has to know I'm leaving".

This topic is closed to new replies.

Advertisement