Sign in to follow this  

Python: Dictionary of Dictionaries

This topic is 4211 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm having some trouble creating a dictionary of dictionaries in python. I can understand how to create a singular dictionary, but the syntax of a dictionary of dictionaries confuse me. Anyone have any advice on how I can go about creating one? Thanks very much!

Share this post


Link to post
Share on other sites
Simply create the dictionaries one at a time. Python dictionaries do not care about what kind of values they contain.

a = {}
a["banana"] = {}

Share this post


Link to post
Share on other sites
Right. Hmm, what Im trying to do is this:

Im trying to create a text based game in python. Lets say I have rooms and players, and each room and player is kept track of by a roomID and playerID. Now, the players can move around in the rooms, and at any time, the user can execute a command (lets say this command is called "Look") to display where the player is. Also, the roomID will contain the room name, and a room description. The playerID will contain the player name, and player description (ie Zelda, A Beautiful Princess). Also, every time the "Look" command is executed, it will display the room name and description, and the player name and description.

What I was thinking of doing was something like this:

player dictionary: - key: roomID
- value: playerID
- key: playerID
- value: playerName, playerDescription

So, in essense...a dictionary of a dictionary?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
Quote:


# 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]


There is no list.find routine. list.index does what you want and throws an exception, although it'd just be easier to use list.remove, which is what you're doing.

Share this post


Link to post
Share on other sites
Hmmm... Dictionaries have time complexity of O(1) and lists have O(n). It would make sense to make a dictionary of dictionaries but the PyNumeric extension has a hardware Array function that would probably work for the inner dictionary thus effectively making it a dictionary of arrays. That might make your code simpler. The only catch is that a Numeric Array is of fixed size so the PlayerName and PlayerDescription would only be elements in the array.

If you want to enumerate the fields of the array so you could refer to them by name rather than by number just make the name a single element tuple (by putting a comma after the constant you are assigning it to) and the tuple will be a constant in your code:

PlayerName=1,
arrayname[PlayerName]="Lancelot"

BTW, I'm not sure about the array syntax since it's been a while since I used Python.

Share this post


Link to post
Share on other sites
Quote:
Original post by bytecoder
Quote:


# 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]


There is no list.find routine. list.index does what you want and throws an exception, although it'd just be easier to use list.remove, which is what you're doing.


Er, yes. Wow, I'm way out of practice :/ Been doing C++ at work :(

Share this post


Link to post
Share on other sites
Ok wow, thanks Zahlman. Hmm, I think your correct in that Im more confused about how to model the situation than the syntax. I just recently started learning Python, so its still a little confusing...and even though the solution you provided is probably most ingenius, Im still not quite sure what is going on with it. XD

Also some clarification, I am also looking to have a "create" function, to allow me to create rooms, players, or items as I am within the game.

So what will happen is this, say I am within the game (all commands to the game are input from the command line):

>>> create room 10 Basement A dark and scary basement

create is the command, room would be referring to the room dictionary, 10 is the room number (key in the room dictionary), Basement is the room name (value in the room dictionary), and "A dark..." is the room description (which, I guess would also be a value in a dictionary, but with the room name as the key for this dictionary).

That would be why I am thinking to use a dictionary of dictionaries for this implementation.

Btw, how do you post code in the posts? XD

Thanks again for your responses. Forgive my newbieness XD

Share this post


Link to post
Share on other sites
Quote:
Original post by fengxSo what will happen is this, say I am within the game (all commands to the game are input from the command line):

>>> create room 10 Basement A dark and scary basement



The room name and description are most logical as members of a Room class. The dictionary can simply map from ID number to instances of this class. A simplified example:


rooms = {}

class Room:
def __init__(self, roomname, roomdesc):
self.roomname = roomname
self.roomdesc = roomdesc

def create(dict, id, roomname, roomdesc):
dict[id] = Room(roomname, roomdesc)

create(rooms, 1234, "Basement", "A dark and scary basement.")





Quote:
Btw, how do you post code in the posts? XD

http://www.gamedev.net/community/forums/faq.asp#tags

[Edited by - Hollower on July 4, 2006 9:42:06 PM]

Share this post


Link to post
Share on other sites
Thanks for everyone's help so far!

Ok, so I have started an early implementation of my game. What I have done so far is to read the commmand line to create a new room for the game.
The command line structure for room creation is explained breifly in an earlier post.

So far, I can successfully read in and parse the command from the command line using regular expressions. However, when I try to assign the parsed variables to a dictionary (in this case, the room dictionary), I get an error saying the key is not found. I have a print statement in the if loop just for testing. And, it will not print anything and give me an error saying the key is not found.

For testing purposes, I am using this as the command:
<Adventure> create room 23 basement A cool basement

import sys
import re

room = {}
player = {}
item = {}

class Room:
def __init__(self, roomname, roomdesc):
self.roomname = roomname
self.roomdesc = roomdesc

class Player:
def __init__(self, playername, desc):
self.playername = playername
self.playerdesc = playerdesc

class Item:
def __init__(self, itemname, desc):
self.itemname = itemname
self.itemdesc = itemdesc

def createRoom(dict, id, roomname, roomdesc):
dict[id] = Room(roomname, roomdesc)

def createPlayer(dict, id, playername, playerdesc):
dict[id] = Player(playername, playerdesc)

def createItem(dict, id, itemname, itemdesc):
dict[id] = Item(itemname, itemdesc)

def prompt():
line = re.compile('(\d+)\s+(\w+)\s+(\w+)')
while 1:
try:
cmd = raw_input("<Adventure> ")
except:
print "Done"
break

if re.search("create room", cmd):
print "Room create"
data = line.search(cmd)
if data:
type_id, name, desc = data.group(1, 2, 3)
createRoom(room, type_id, name, desc)
print "ID: %s, name: %s, desc: %s" % (type_id, name, desc)
print room[23]
elif re.search("create item", cmd):
print "item create"
elif re.search("create player", cmd):
print "player create"
else:
print "Unknown command!"

prompt()




Also, I am not able to take in the entire line of "A cool basement" I can only take in "A". I know I should not be using \w to parse the regular expression, but I cant think of what to use in place of that...

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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"?

Share this post


Link to post
Share on other sites
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__.

Share this post


Link to post
Share on other sites
Ohhh I see! Thats great. Thanks for your help ^^

Learning something new everyday :D

Share this post


Link to post
Share on other sites
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 description

class 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?

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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)


Share this post


Link to post
Share on other sites
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".

Share this post


Link to post
Share on other sites

This topic is 4211 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this