I haven't built many networked games, but I've always struggled with client side object creation. I always hate the idea of passing a string, or enum, or some sort of identifier that is then ran through a switch statement to see which object to create. Breakneck Brigade had a pretty clever way to do this in c# using the activator class. We had a static function called deserialize that would unpack the byte stream and dynamically pick which constructor to call using activator.

GameObjectClass cls = (GameObjectClass)reader.ReadInt16();
Type type = cls.GetGameObjectType();
return (ClientGameObject)Activator.CreateInstance(type, id, reader, game);

However, Type is still just an enum we needed to parse and match to an actual class.

public static Type GetGameObjectType(this GameObjectClass objClass)
{
    switch (objClass)
    {
        case GameObjectClass.Ingredient:
            return typeof(ClientIngredient);
        case GameObjectClass.Cooker:
            return typeof(ClientCooker);
        case GameObjectClass.Terrain:
            return typeof(ClientTerrain);
        case GameObjectClass.StaticObject:
            return typeof(ClientStaticObject);
        case GameObjectClass.Player:
            return typeof(ClientPlayer);
        default:
            throw new Exception("GetGameObjectType not defined for " + objClass.ToString());
    }
}

It worked well, mainly because we only had a few objects. Every time we added a new object though, we consistently forgot to add the necessary glue in the enum. It was a small amount of over head but it was still enough to trip us up. In python though, there is nice function called getattr.

Given a string and an object, getattr returns the attribute of that object. If you have object x, and give it the string 'foobar', using getattr, you will get the equivalent of x.foobar. This works for any object. So you can pass it something like a module and a string that is the name of a class and get the constructor. This opens up a whole new level of automation. Rather than having to switch on an enum, we can simply send the class name to the client and have the client check our world module for the class and use it as the constructor.

Server code, looping over game object list to build the packet

send_list = []
for game_obj in obj_list:
  send_dict = {"rect": [game_obj.rect.x, game_obj.rect.y, game_obj.rect.width,
                        game_obj.rect.height], "id": game_obj.id, "color": game_obj.color,
               "constructor": type(game_obj).__name__}
  send_list.append(send_dict)

Client, reading the packet and using the constructor string

import world as wd # holds all our constructors for our games
....
for game_obj in obj_list:
    constructor = getattr(wd, game_obj['constructor'])
    self.game_objects[game_obj['id']] = constructor(game_obj['rect'][0],game_obj['rect'][1],
                                                    game_obj['rect'][2], game_obj['rect'][3],
                                                    color=game_obj['color'], obj_id=game_obj['id'])

Now the client has a dictionary called game_objects with dynamically constructed game objects. It isn't a perfect solution though, and introduces a few problems.

The first is that we need to keep all our game_objects in one file, world(wd). This can either be a big problem or not a problem at all, depending on how you like to program. If you want lots of files with a single class each, this is more inconvienent. Personally I use PyCharm and having a large file isn't a problem, I just use the nav tree to go to the class I need. There is still a few options, though we do lose some elegance. Keep a list of the modules that have constructor functions, loop over the lists and try to find the constructor named. Something like

import world as wd 
import player as pl
module_list = [wd, pl]  # a list of all modules that contain game all object constructors

for game_obj in obj_list:
    for module in module_list:
      if hasattr(module, game_obj['constructor']):
        constructor = getattr(wd, game_obj['constructor'])
        break
    self.game_objects[game_obj['id']] = constructor(game_obj['rect'][0],game_obj['rect'][1],
                                                    game_obj['rect'][2], game_obj['rect'][3],
                                                    color=game_obj['color'], obj_id=game_obj['id'])

This is a little more overhead than I would want but I still find it better than having a giant switch statement somewhere. It is possible to abstract this even more, passing in the specific module I want by string and using something like locals()['module'] to get the module by string, but for now I like the verbosity of keeping track of which modules I'm allowing a constructor from. If you really want to get full automation, you can skip importing anything on the client side and have server send all the modules the client will need, however sometimes being dynamic can be a little to crazy