[Solved] Storing entities of different types in a list


I have several classes like Character, Weapon, etc. in my game. I want to be able to access all instances of these classes by storing them in one single list, because the player can ‘activate’ them by pressing a key (‘activation’ is to mean: Characters can be spoken to, Weapons can be picked up, etc.).
Now, in C++ I would create some ActivatorBase class that contains an activate method and have the above classes inherit from it, then create a list of base pointers. But how would I go about it in Python :confused: ?

I hope that someone is able to help me, I am stuck.

Python’s list (and dictionaries etc.) can hold whatever you put in them. A single list is not limited to a single type, you can mix them.

Oh! I did not reckon with that! Well, is there a possibility to explicitly tell Python of which type a variable is (like typecasting)?
Because I tried creating a dictionary to map Characters and Weapons to string identifiers. Both classes contain the method activate(), but when I do this:

objects = {}

objects["character1"] = Character()
objects["weapon1"] = Weapon()


it always treats objects[“weapon”] as a Character and therefore calls Character.activate() instead of Weapon.activate().

I do it like:

objects = {}
id = 0

objects[id] = [Character(arg), Weapon(arg)]
id += 1


So there has to be something wrong in your code. To see that it really works (and just works, with no need to tell python anything), try making a dictionary of strings, ints and floats and then making some operations on them or just printing their types with print type(key). If you make your classes correctly they should work just the same.

I have tried what you have suggested: First with ints, floats and strings, then with my classes. I think I found out where my mistake lies, but I do not know how to do it correctly:

class CCharacter:
    def __init__(self, model):
        global nextID
        self.id = nextID
        nextID += 1		

        entities[self.id] = self


entities is a global dictionary. I think the mistake is that I put self into it, while I should do something else … Any suggestions?

Actually, if you want to store this class object then you should pass “self”, just like you did. To be honest, that code looks fine.

The code is propably correct, but I wonder why you do the class instance passing from the “inside”. I do it from the “outside”:

from characters import Character
from weapons import Weapon

class World():
	def __init__(self, arg):
		self.world_objects = {}
		self.world_id = 0
		for i in range(5):
			self.world_objects[i] = [Character(self.world_id), Weapons(self.world_id)]
			self.world_id += 1

You can then adress things by:

my_char = self.world_objects[id][0]
my_weapon = self.world_objects[id][1]

I think what happens here:

objects = {}
objects["character1"] = Character()
objects["weapon1"] = Weapon()


it always treats objects["weapon"] as a Character and therefore calls Character.activate() instead of Weapon.activate().

is that you first assign an instance of Character to the key “character1”, then an instance of the Weapon class to a different key, instead of adding it as another value for the first key.

I think that’s merely a matter of design. Maybe at this point in the code the Weapon is not associated with the Character? For instance, in my game I store all the stuff on my map that way for saving/loading/destruction purposes. If the Weapon happens to be a pickable/usable object just resting somewhere on the map, then at the time of initialization it’s not associated with any character (or anything else except for the scene itself), but it has to be stored somewhere.

In any case, whether you store stuff as “one-level” dictionary or a “multi-level” dictionary of lists, it should work either way. For the language it doesn’t make any difference.

Fero63, if it’s not confidential, maybe you could just post the whole relevant code (or it’s simplified version) here so we can take a look?

Also, try printing out the whole dictionary (or list, or whatever you chose to work with) of your class objects. It’ll show which object of which type is stored where and that, combined with the code that creates the data structure, should give clear view on what goes wrong and where.

OK, now it works. I cannot really explain why Python behaves like this, but…well: I have all my global variables in a seperate module called globals. Now for one reason or another if I import it like this:

from globals import *

and then use nextID (see my previous post) like that:

global nextID
nextID += 1

nextID gets not incremented :confused:
If I instead import only the module (and not the definitions) like this:

import globals

and then use nextID like that:

globals.nextID += 1

it works perfectly.

If anyone can explain why that is: You may speak :wink:

from globals import *

nextID is imported to the module, it’s the same way as this assignment :

import globals
nextID = globals.nextID

It duplicates nextID (not refer to the same integer object)
So whatever you do later on, it happens on the copy.

If another module also does that, you get another copy of nextID and operate on that, unless you save it back to your globals module, as you did in the #2 way.

That’s the assignment rule of integer, float, string, and maybe some other “basic” python object types.
For the rest of the types, eg. sequences (tuple, list, deque, etc.), assignment is done by reference instead (no copy is involved).

Interesting. Thank you for pointing that out to me.
And thanks to coppertop and Bradamante, as well. You guys have really helped me :slight_smile:

See, you and I both had problems understanding that concept :smiley:

panda3d.org/forums/viewtopic … 3375#43375

What a coincidence :open_mouth: Seriously, I believe a lot of people have that problem. It is a confusing (and to me counterintuitive) concept in Python, which is otherwise a great and easy-to-learn language.