pickle

Dillinlagda gurkor*

Dillinlagda gurkor*

Till mitt lilla spel ville jag lägga in möjligheten att spara och ladda spel och funderade länge på hur man gjorde det på bästa sätt. Jag hittade efter ett tag pickle i pythons standardbibliotek som visade sig vara ett enkelt sätt att hantera detta på.

Det pickle-modulen gör är att serialisera python-objekt till en fil. Med detta menas att pickle omvandlar objektet till en serie bytes som sedan kan skrivas till fil. Man kan jämföra det med att i C dumpa en struct via

typedef struct {
int pos[2];
int size[2];
char name[256];
} data_t

data_t d;
write(file, d, sizeof(d));

Detta fungerar för struct:ar som enbart innehåller data, men innehåller den referenser blir det hela mer komplicerat och kräver särskild implementation. Eftersom python ser allt som objekt, blir Pickle lite mer intelligent och serialiserar all data som lagras i objektet samt alla andra objekt som objektet hänvisar till (rekursivt hela vägen ner till basobjektet object) samt funktionerna (kod är också objekt) som ingår i objektet.

För att spara all data i mitt spelarobjekt räcker det med

pickle.dump(player, open(player.save_slot + "/player.data", 'wb'))

vilket ser ganska enkelt ut, tyvärr finns det komplikationer. När jag försökte detta första gången fick jag följande felmeddelande

TypeError: can’t pickle Surface objects

Detta beror på att objektet måste ha bland andra metoderna __getstate__() och __setstate__() för att läsa ut samtliga objekt i objektet och pygame-objekten saknar detta (åtminstone Surface och Sound och i dessa fall är det nog vettigt att hantera utanför pickle pga. datamängden).

För att gå runt det här problemet måste de felande objekten raderas från listan av objekt som sparas och sedan manuellt återställas när objekten laddas in igen. Detta görs genom att implementera en egen variant av __getstate__() och __setstate__():

def __getstate__(self):
print 'getting state!'
state = dict(self.__dict__)
#remove unpicklables
state = {s:state[s] for s in state if s != pygame.Surface and s != pygame.Sound}
return state

def __setstate__(self, state):
self.__dict__ = state
self.a = animate.animationGroup()
self.restore_unpicklables()

När pickle.dump sparar objektet anropas mitt objekts __getstate__() och innan uppslagsverket med alla objekt returneras plockas alla objekt som inte kan serialiseras bort (här pygame.Surface-objekt och pygame.Sound-objekt). När sedan objektet skall återställas kallar pickle.load()__setstate__() för att återställa objektets interna uppslagsverk anropas funktioner för att återskapa de borttagna objekten.

Det här metoden att hantera sparfiler på har både brister och fördelar men fungerar bra under utvecklingen och tog en halvtimme att skriva ihop. Den stora fördelen var att jag snabbt kunde komma igång och få någonting som fungerar. Den stora nackdelen är att spelarobjektets blir exakt som det var när det sparades och ifall variabler eller nya funktioner tillkommit kommer dessa saknas vilket kan leda till strul. Sedan bör man nämna att picklade objekt är relativt osäkra i och med att allt lagras i ett helt öppet format och inga kontroller genomförs innan datan laddas in.

* Bilden kommer härifrån, rekommenderas för inspiration för heminlagda gurkor!

Etiketter: , ,

Kommentera

Fyll i dina uppgifter nedan eller klicka på en ikon för att logga in:

WordPress.com Logo

Du kommenterar med ditt WordPress.com-konto. Logga ut / Ändra )

Twitter-bild

Du kommenterar med ditt Twitter-konto. Logga ut / Ändra )

Facebook-foto

Du kommenterar med ditt Facebook-konto. Logga ut / Ändra )

Google+ photo

Du kommenterar med ditt Google+-konto. Logga ut / Ändra )

Ansluter till %s


%d bloggare gillar detta: