Using a unpickled Dict like a module

In my game I am using dictionaries like a database, which get imported as modules. For example:

# dbships.py

ShipList = {"ship1":[data], "ship2":[data]}

and then to import the data:

# world.py

from dbships import ShipList

class World():
	this_ship_data = ShipList["ship1"]

Now the databases have become so large that I can’t manage them by hand anymore so I’d like to create some little GUI tool to access them. I already have that for save games. Pilot data in the game is a dict that I pickle into a file to save them. Works fine.

I’d like to do the same now with the databases. The problem is that for the pilot files I bind the result of the unpickle to an object where as for the databases I use module imports. That makes an object like ShipList available everywhere. How can I achieve that for the databases, once they are loaded via pickle, like the pilots are?

I couldn’t do:

# world.py

ShipList = pickle.load(dbships)

class World():
	this_ship_data = ShipList["ship1"]

could I? I had to use the global keyword everywhere, right? Which as far as I know is a bad sign? I could do:

# myglobals.py
    (empty file)

# world.py

import myglobals

myglobals.databases.ShipList = pickle.load(dbships)
myglobals.databases.OtherDatabase = ...
# more unpickling here ...

class World():
	this_ship_data = myglobals.databases.ShipList["ship1"]

Would that be the way to go?

I would do something like this:

class Game():
    def __init__(self):
        self.shiplist = pickle.load(whatever)
        ship1 = Ship(self, 'ship1')


class Ship():
    def __init__(self, game, template):
        self.game = game
        self.this_ship_data = self.game.shiplist[template]

Alright, I guess this is the point to do some re-structuring.

I guess I will use the “commons” strategy that is mentioned in the Python literature: put a lot of

from assets import ThisAsset
myasset = ThisAsset

stuff in a file named commons.py.
Then, in other files, use

import commons
commons.myasset.doStuff()

In the past I thought this would get me circular imports, but I guess doing it right (this time) will avoid them.
Still, I got a question. Right not, I got a lot of stuff in the init functions, like in the Environment class. So, that’s visible stuff depending on a lot of output from the outside.

When I do

# commons.py
from assets import ThisAsset # this is a class with a lot in it's __init__()
myasset = ThisAsset

the instanced class remains “dead”, i.e. init does not get executed, which in my case is good. So, later I would do

# game.py
import commons
commons.myasset()

to “execute” the ThisAsset class, thus calling it’s init. Or, should I clean up all my init’s, moving the stuff in a buildXYZ function?

# commons.py
from assets import ThisAsset # this is a class with an empty __init__()
myasset = ThisAsset() # will execute empty __init__()

# game.py
import commons
commons.myasset.buildAsset()

In the past I tried that, but abonded it for some reason. I think it was too hard to manage the variables required by the (empty) init() function vs. the buildAsset() function.

Any ideas?

When you do this:

from assets import ThisAsset
myasset = ThisAsset

It is not making an instance of the class, it is just two variables pointing to the class, unless you meant to put myasset = ThisAsset().

Assuming you meant to put () after ThisAsset, when you do this:

import commons
commons.myasset.buildAsset()

You will only ever have one instance of the class. If this is supposed to represent some game object like a ship you should have one instance of the class per ship, so it would look like:

import commons
ship1 = commons.ThisAsset()
ship2 = commons.ThisAsset()

The init function should be taking care of all the work to create this object, so I am a bit confused as to what your buildAsset function would be for and why you would not want the init to run.

Yeah, I know, that’s what I am doing right now. And no, the : are not missing. I thought by just pointing to a class you can avoid calling it’s init function.

# globals.py
(empty)

# game.py

from environment import Environment
import globals

class Game():
	mcglobals.env = Environment()

# environment.py

import globals

class Environment():
	def __init__():
		# do all kinds of visible stuff here

The problem - and the original motivation to do the re-structuring - is stuff like this … given the code above:

# environment.py

import globals
from ship import Ship

class Environment():
	def __init__():
		# more stuff
		
	def addShips():
		ship = Ship()
		
# ship.py

import globals

class Ship():
	def __init():
		newtarget = mcglobals.env.Node # crash

which crashes saying something like:
attribute “env” does not exist in module globlas
or something like that.
Which I guess has something to do with the order in which the imports happen in all kind of my files. I fiddled around a bit with that, but over time it’s just too trouble-some to manage.

At some point in time I did use the technique you mentioned.

# ship.py
from aifsm import BasicFSM

class Ship():
	__init__():
		self.AI_FSM = BasicFSM(self)
		
# aifsm.py

class BasicFSM():
	__init__(parent):
		self.ParentShip = parent

which works fine, but might again be hard to manage. And I look at a lot of reference Python code, on the internet and in the literature, and I don’t see that self-passing technique used much. What I do see a lot is the singleton design pattern via the

import myglobals
myglobals.stuff = Stuff()

or the commons idiom mentioned above, which I guess I will go for. But always when I do stuff like this there’s always some problem I don’t expect.

Just to give an example why I don’t like managing the self-passing technique … In my game I got a Player class, which starts the environment. The player also holds the save game data, which has to be edited by the environment sometimes. The environment holds the collision traverser and spawns ships. The ships spawn shot, which have to be added to the environment’s collision traverser. The shot function is in some code collection file.

So, you got:

Menu -> Player <-> Environment
Environment -> Ship -> Shot
Shot -> Environment

Until now this is done by using the globals approach almost exclusively. Let me give some pseudo code using a mix of globals and self-passing:

# globals.py

(empty)

# menu.py

import globals
from player import Player

savedata = {}

globals.player = Player(savedata)

# player.py

import globals
from environment import Environment

class Player()
	__init__(savedata):
		globals.savedata = savedata # this would be using the globals approach
		self.savedata = savedata # to use the self-passing approach
		
		globals.environment = Environment() # using the globals approach
		globals.environment = Environment(self) # using the self-passing approach
		
# environment.py

import globals
from ship import Ship

class Environment()
	__init__(player): # from the self-passing
		self.player = player # self-passing
		
		colltrav = CollisionTraverser()
		ship = Ship(self) # self-passing
		
	def editsave():
		mcglobals.savedata[key] = value # globals
		self.player.savedata[key] = value # self-passing
		
# ship.py

import globals
import code

class Ship()
	__init__(environment):
		model = loader.loadModel()
		
	def fireshot():
		code.fireshot(self, environment.colltrav)
		
# code.py

fireshot(ship, colltrav):
	shot = loader.loadModel()
	shot.setPos(ship.model.getPos()
	
	shotcnode = CollisionNode()
	colltrav.addCollider(shotcnode)

That’s what I mean: Using the self-passing idiom I am passing self twice instead of using a no-brainer globals or commons approach.

“globals” is a special Python variable, so there is a good chance you had trouble because your module was named globals.

True, you probably won’t see the self-passing mentioned much. The “proper” way would be to create an instance of the class, say Ship, then call a function on the Ship like my_ship.set_level(my_level). I just found passing it in the init to be more convenient. You do see passing of a “parent” type object to init often though, for example in ODE.