pickle — Python-objektserialisering

Källkod: Lib/pickle.py


Modulen pickle implementerar binära protokoll för serialisering och de-serialisering av en Python-objektstruktur. ”Pickling” är processen där en Python-objekthierarki konverteras till en byte-ström, och ”unpickling” är den omvända operationen, där en byte-ström (från en binär fil eller bytesliknande objekt) konverteras tillbaka till en objekthierarki. Pickling (och unpickling) kallas även ”serialization”, ”marshalling”, [1] eller ”flattening”, men för att undvika förvirring används här termerna ”pickling” och ”unpickling”.

Varning

Modulen pickle är inte säker. Plocka endast upp data som du litar på.

Det är möjligt att konstruera skadlig pickle-data som exekverar godtycklig kod under uppackning. Plocka aldrig upp data som kan ha kommit från en icke betrodd källa eller som kan ha manipulerats.

Överväg att signera data med hmac om du behöver försäkra dig om att de inte har manipulerats.

Säkrare serialiseringsformat som json kan vara lämpligare om du bearbetar otillförlitliga data. Se Jämförelse med json.

Förhållande till andra Python-moduler

Jämförelse med marshal

Python har en mer primitiv serialiseringsmodul som heter marshal, men i allmänhet bör pickle alltid vara det föredragna sättet att serialisera Python-objekt. marshal finns främst för att stödja Pythons .pyc-filer.

Modulen pickle skiljer sig från marshal på flera viktiga sätt:

  • Modulen pickle håller reda på de objekt som den redan har serialiserat, så att senare referenser till samma objekt inte behöver serialiseras igen. marshal gör inte detta.

    Detta har konsekvenser för både rekursiva objekt och objektdelning. Rekursiva objekt är objekt som innehåller referenser till sig själva. Dessa hanteras inte av marshal, och i själva verket kommer försök att marshalera rekursiva objekt att krascha din Python-tolk. Objektdelning sker när det finns flera referenser till samma objekt på olika platser i objekthierarkin som serialiseras. pickle lagrar sådana objekt endast en gång och ser till att alla andra referenser pekar på huvudkopian. Delade objekt förblir delade, vilket kan vara mycket viktigt för föränderliga objekt.

  • marshal kan inte användas för att serialisera användardefinierade klasser och deras instanser. pickle kan spara och återställa klassinstanser på ett transparent sätt, men klassdefinitionen måste vara importerbar och finnas i samma modul som när objektet lagrades.

  • Serialiseringsformatet marshal garanteras inte att vara portabelt mellan olika Python-versioner. Eftersom dess primära uppgift är att stödja .pyc-filer, förbehåller sig Python-implementatörerna rätten att ändra serialiseringsformatet på icke-bakåtkompatibla sätt om behov skulle uppstå. Serialiseringsformatet pickle är garanterat bakåtkompatibelt mellan Python-versioner förutsatt att ett kompatibelt pickle-protokoll väljs och att pickling- och unpickling-koden hanterar typskillnader mellan Python 2 och Python 3 om dina data passerar den unika språkgränsen.

Jämförelse med json

Det finns grundläggande skillnader mellan pickle-protokollen och JSON (JavaScript Object Notation):

  • JSON är ett serialiseringsformat för text (det matar ut unicode-text, även om det för det mesta sedan kodas till utf-8), medan pickle är ett binärt serialiseringsformat;

  • JSON är läsbart för människor, medan pickle inte är det;

  • JSON är interoperabelt och används ofta utanför Python-ekosystemet, medan pickle är Python-specifikt;

  • JSON kan som standard bara representera en delmängd av Pythons inbyggda typer, och inga anpassade klasser; pickle kan representera ett extremt stort antal Python-typer (många av dem automatiskt, genom smart användning av Pythons introspektionsmöjligheter; komplexa fall kan hanteras genom att implementera specifika objekt-API:er);

  • Till skillnad från pickle skapar deserialisering av icke betrodda JSON inte i sig en sårbarhet för godtycklig kodkörning.

Se även

Modulen json: en standardbiblioteksmodul som möjliggör serialisering och deserialisering av JSON.

Format för dataström

Dataformatet som används av pickle är Python-specifikt. Fördelen med detta är att det inte finns några begränsningar från externa standarder som JSON (som inte kan representera delning av pekare), men det innebär att program som inte är Python-program kanske inte kan rekonstruera Python-objekt med pickles.

Som standard använder pickle dataformatet en relativt kompakt binär representation. Om du behöver optimala storleksegenskaper kan du på ett effektivt sätt komprimera pickled data.

Modulen pickletools innehåller verktyg för att analysera dataströmmar som genereras av pickle. Källkoden till pickletools har omfattande kommentarer om opkoder som används av pickle-protokoll.

Det finns för närvarande 6 olika protokoll som kan användas för betning. Ju högre protokoll som används, desto nyare version av Python behövs för att läsa den pickle som produceras.

  • Protokollversion 0 är det ursprungliga ”mänskligt läsbara” protokollet och är bakåtkompatibelt med tidigare versioner av Python.

  • Protokollversion 1 är ett gammalt binärt format som också är kompatibelt med tidigare versioner av Python.

  • Protokollversion 2 introducerades i Python 2.3. Det ger mycket effektivare betning av nya stilklasser. Se PEP 307 för information om förbättringar som protokoll 2 medför.

  • Protokollversion 3 lades till i Python 3.0. Det har uttryckligt stöd för bytes-objekt och kan inte avplockas av Python 2.x. Detta var standardprotokollet i Python 3.0–3.7.

  • Protokollversion 4 lades till i Python 3.4. Det lägger till stöd för mycket stora objekt, pickling av fler typer av objekt och vissa dataformatoptimeringar. Detta var standardprotokollet i Python 3.8–3.13. Se PEP 3154 för information om förbättringar som protokoll 4 medför.

  • Protokollversion 5 lades till i Python 3.8. Det ger stöd för data utanför bandet och ökad hastighet för data inom bandet. Det är standardprotokollet från och med Python 3.14. Se PEP 574 för information om förbättringar som protokoll 5 medför.

Anteckning

Serialisering är ett mer primitivt begrepp än persistens; även om pickle läser och skriver filobjekt, hanterar den inte frågan om namngivning av persistenta objekt eller den (ännu mer komplicerade) frågan om samtidig åtkomst till persistenta objekt. Modulen pickle kan omvandla ett komplext objekt till en byte-ström och den kan omvandla byte-strömmen till ett objekt med samma interna struktur. Det kanske mest uppenbara att göra med dessa byteflöden är att skriva dem till en fil, men det är också tänkbart att skicka dem över ett nätverk eller lagra dem i en databas. Modulen shelve ger ett enkelt gränssnitt för att plocka och plocka upp objekt på DBM-liknande databasfiler.

Modulgränssnitt

För att serialisera en objekthierarki anropar du helt enkelt funktionen dumps(). På samma sätt anropar du funktionen loads() för att avserialisera en dataström. Men om du vill ha mer kontroll över serialisering och de-serialisering kan du skapa ett Pickler- respektive ett Unpickler-objekt.

Modulen pickle tillhandahåller följande konstanter:

pickle.HIGHEST_PROTOCOL

Ett heltal, den högsta protocol version som finns tillgänglig. Detta värde kan skickas som ett protocol-värde till funktionerna dump() och dumps() samt till Pickler-konstruktören.

pickle.DEFAULT_PROTOCOL

Ett heltal, standard protokollversion som används för betning. Kan vara mindre än HIGHEST_PROTOCOL. För närvarande är standardprotokollet 5, introducerat i Python 3.8 och inkompatibelt med tidigare versioner. Denna version introducerar stöd för out-of-band-buffertar, där PEP 3118-kompatibla data kan överföras separat från huvudflödet i pickle.

Ändrad i version 3.0: Standardprotokollet är 3.

Ändrad i version 3.8: Standardprotokollet är 4.

Ändrad i version 3.14: Standardprotokollet är 5.

Modulen pickle tillhandahåller följande funktioner för att göra betningsprocessen mer bekväm:

pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)

Skriver den picklade representationen av objektet obj till det öppna file object file. Detta är likvärdigt med Pickler(file, protocol).dump(obj).

Argumenten file, protocol, fix_imports och buffer_callback har samma betydelse som i Pickler-konstruktorn.

Ändrad i version 3.8: Argumentet buffer_callback har lagts till.

pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)

Returnerar den betade representationen av objektet obj som ett bytes-objekt, istället för att skriva det till en fil.

Argumenten protocol, fix_imports och buffer_callback har samma betydelse som i Pickler-konstruktorn.

Ändrad i version 3.8: Argumentet buffer_callback har lagts till.

pickle.load(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

Läser in den inlagda representationen av ett objekt från den öppna file object file och returnerar den rekonstruerade objekthierarkin som anges där. Detta är ekvivalent med Unpickler(file).load().

Protokollversionen av pickle upptäcks automatiskt, så inget protokollargument behövs. Bytes som ligger efter objektets picklade representation ignoreras.

Argumenten file, fix_imports, encoding, errors, strict och buffers har samma betydelse som i konstruktören Unpickler.

Ändrad i version 3.8: Argumentet buffertar har lagts till.

pickle.loads(data, /, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

Returnerar den rekonstituerade objekthierarkin för den betade representationen data av ett objekt. data måste vara en bytesliknande objekt.

Protokollversionen av pickle upptäcks automatiskt, så inget protokollargument behövs. Bytes som ligger efter objektets picklade representation ignoreras.

Argumenten fix_imports, encoding, errors, strict och buffers har samma betydelse som i konstruktören Unpickler.

Ändrad i version 3.8: Argumentet buffertar har lagts till.

Modulen pickle definierar tre undantag:

exception pickle.PickleError

Gemensam basklass för de andra betningsundantagen. Den ärver från Exception.

exception pickle.PicklingError

Fel som uppstår när ett objekt som inte går att plocka upp påträffas av Pickler. Det ärver från PickleError.

Se Vad kan vara inlagt och oinlagt? för att lära dig vilka typer av objekt som kan picklas.

exception pickle.UnpicklingError

Fel som uppstår när det finns ett problem med att plocka upp ett objekt, t.ex. datakorruption eller säkerhetsöverträdelse. Det ärver från PickleError.

Observera att andra undantag också kan uppstå under unpickling, inklusive (men inte nödvändigtvis begränsat till) AttributeError, EOFError, ImportError och IndexError.

Modulen pickle exporterar tre klasser, Pickler, Unpickler och PickleBuffer:

class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)

Detta tar en binär fil för att skriva en pickle-dataström.

Det valfria argumentet protocol, ett heltal, talar om för betningsmaskinen att den ska använda det angivna protokollet; protokoll som stöds är 0 till HIGHEST_PROTOCOL. Om det inte anges är standardvärdet DEFAULT_PROTOCOL. Om ett negativt tal anges väljs HIGHEST_PROTOCOL.

Argumentet file måste ha en write()-metod som accepterar ett enda bytes-argument. Det kan alltså vara en fil på disken som är öppen för binär skrivning, en io.BytesIO-instans eller något annat anpassat objekt som uppfyller detta gränssnitt.

Om fix_imports är true och protocol är mindre än 3, kommer pickle att försöka mappa de nya Python 3-namnen till de gamla modulnamnen som används i Python 2, så att pickle-dataflödet kan läsas med Python 2.

Om buffer_callback är None (standard), serialiseras buffertvyer till file som en del av pickle-strömmen.

Om buffer_callback inte är None kan den anropas hur många gånger som helst med en buffertvy. Om callbacken returnerar ett falskt värde (t.ex. None), är den givna bufferten out-of-band; annars serialiseras bufferten in-band, dvs. inuti pickle-strömmen.

Det är ett fel om buffer_callback inte är None och protocol är None eller mindre än 5.

Ändrad i version 3.8: Argumentet buffer_callback har lagts till.

dump(obj)

Skriv den betade representationen av obj till open file object som anges i konstruktören.

persistent_id(obj)

Gör ingenting som standard. Detta finns så att en underklass kan åsidosätta det.

Om persistent_id() returnerar None betas obj som vanligt. Alla andra värden gör att Pickler skickar ut det returnerade värdet som ett persistent ID för obj. Betydelsen av detta persistenta ID bör definieras av Unpickler.persistent_load(). Observera att det värde som returneras av persistent_id() inte själv kan ha ett persistent ID.

Se Persistens för externa objekt för detaljer och exempel på användningsområden.

Ändrad i version 3.13: Lägg till standardimplementationen av den här metoden i C-implementationen av Pickler.

dispatch_table

Ett picklerobjekts dispatch table är ett register över reduktionsfunktioner av den typ som kan deklareras med copyreg.pickle(). Det är en mappning vars nycklar är klasser och vars värden är reduktionsfunktioner. En reduktionsfunktion tar ett enda argument från den associerade klassen och bör överensstämma med samma gränssnitt som en __reduce__()-metod.

Som standard kommer ett pickler-objekt inte att ha ett dispatch_table-attribut, och det kommer istället att använda den globala dispatch-tabellen som hanteras av copyreg-modulen. För att anpassa betningen för ett specifikt betningsobjekt kan man dock ställa in attributet dispatch_table till ett diktliknande objekt. Alternativt, om en underklass av Pickler har ett dispatch_table attribut så kommer detta att användas som standard dispatch table för instanser av den klassen.

Se Expeditionsbord för användningsexempel.

Tillagd i version 3.3.

reducer_override(obj)

Speciell reducerare som kan definieras i Pickler subklasser. Denna metod har prioritet över alla reducerare i dispatch_table. Den bör överensstämma med samma gränssnitt som en __reduce__()-metod, och kan eventuellt returnera NotImplemented för att fallbacka på dispatch_table-registrerade reducerare för att plocka obj.

För ett detaljerat exempel, se Anpassad reduktion för typer, funktioner och andra objekt.

Tillagd i version 3.8.

fast

Föråldrad. Aktiverar snabbläge om det sätts till ett sant värde. Det snabba läget inaktiverar användningen av memo, vilket påskyndar betningsprocessen genom att inte generera överflödiga PUT-opkoder. Det bör inte användas med självrefererande objekt, annars kommer Pickler att rekursera oändligt.

Använd pickletools.optimize() om du behöver mer kompakta pickles.

clear_memo()

Rensar picklerns ”memo”.

Memot är den datastruktur som kommer ihåg vilka objekt som picklern redan har sett, så att delade eller rekursiva objekt picklas per referens och inte per värde. Denna metod är användbar vid återanvändning av picklers.

class pickle.Unpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict', buffers=None)

Detta tar en binär fil för att läsa en pickle-dataström.

Protokollversionen av pickle identifieras automatiskt, så inget protokollargument behövs.

Argumentet file måste ha tre metoder, en read()-metod som tar ett heltalsargument, en readinto()-metod som tar ett buffertargument och en readline()-metod som inte kräver några argument, som i gränssnittet io.BufferedIOBase. Således kan file vara en diskfil som öppnas för binär läsning, ett io.BytesIO-objekt eller något annat anpassat objekt som uppfyller detta gränssnitt.

De valfria argumenten fix_imports, encoding och errors används för att styra kompatibilitetsstödet för pickle-strömmar som genererats av Python 2. Om fix_imports är true kommer pickle att försöka mappa de gamla Python 2-namnen till de nya namn som används i Python 3. encoding och errors talar om för pickle hur man avkodar 8-bitars stränginstanser som plockats upp av Python 2; dessa är standardiserade till ’ASCII’ respektive ’strict’. encoding kan vara ’bytes’ för att läsa dessa 8-bitars stränginstanser som bytes-objekt. Användning av encoding='latin1' krävs för att avplocka NumPy-arrayer och instanser av datetime, date och time som avplockats av Python 2.

Om buffers är None (standard) måste all data som behövs för deserialisering finnas i pickle-strömmen. Detta innebär att argumentet buffer_callback var None när en Pickler instantierades (eller när dump() eller dumps() anropades).

Om buffers inte är None, bör det vara en iterabel av buffertaktiverade objekt som konsumeras varje gång pickle-strömmen refererar till en out-of-band buffertvy. Sådana buffertar har givits i ordning till buffer_callback för ett Pickler-objekt.

Ändrad i version 3.8: Argumentet buffertar har lagts till.

load()

Läser den betade representationen av ett objekt från det öppna filobjektet som anges i konstruktören och returnerar den rekonstruerade objekthierarkin som anges däri. Byte efter den betade representationen av objektet ignoreras.

persistent_load(pid)

Ger som standard upphov till ett UnpicklingError.

Om definierat bör persistent_load() returnera det objekt som anges av det beständiga ID:t pid. Om ett ogiltigt beständigt ID påträffas bör ett UnpicklingError uppstå.

Se Persistens för externa objekt för detaljer och exempel på användningsområden.

Ändrad i version 3.13: Lägg till standardimplementationen av denna metod i C-implementationen av Unpickler.

find_class(module, name)

Importera modul om det behövs och returnera objektet som heter namn från den, där argumenten modul och namn är str-objekt. Observera att, till skillnad från vad namnet antyder, används find_class() också för att hitta funktioner.

Underklasser kan åsidosätta detta för att få kontroll över vilken typ av objekt och hur de kan laddas, vilket potentiellt kan minska säkerhetsriskerna. Se Begränsning av globaler för mer information.

Utlöser en auditing event pickle.find_class med argumenten module, name.

class pickle.PickleBuffer(buffer)

Ett omslag för en buffert som representerar betningsbar data. buffer måste vara ett buffer-providing-objekt, t.ex. ett bytesliknande objekt eller en N-dimensionell array.

PickleBuffer är i sig en buffertleverantör, därför är det möjligt att skicka den till andra API:er som förväntar sig ett buffertlevererande objekt, t.ex. memoryview.

PickleBuffer-objekt kan endast serialiseras med pickle protokoll 5 eller högre. De är kvalificerade för out-of-band serialization.

Tillagd i version 3.8.

raw()

Returnerar en memoryview av minnesområdet som ligger bakom denna buffert. Det returnerade objektet är en endimensionell, C-sluten minnesvy med format B (osignerade byte). BufferError uppstår om bufferten varken är C- eller Fortran-sluten.

release()

Frigör den underliggande bufferten som exponeras av PickleBuffer-objektet.

Vad kan vara inlagt och oinlagt?

Följande typer kan picklas:

  • inbyggda konstanter (None, True, False, Ellipsis och NotImplemented);

  • heltal, flyttal, komplexa tal;

  • strängar, byte, bytearrayer;

  • tupler, listor, mängder och lexikon som endast innehåller plockbara objekt;

  • funktioner (inbyggda och användardefinierade) som är tillgängliga från den översta nivån i en modul (med def, inte lambda);

  • klasser som är tillgängliga från den översta nivån i en modul;

  • instanser av sådana klasser vars resultat av anropet __getstate__() är picklable (se avsnitt Beizning av klassinstanser för detaljer).

Försök att plocka ut icke-plockbara objekt kommer att ge upphov till undantaget PicklingError; när detta händer kan ett ospecificerat antal byte redan ha skrivits till den underliggande filen. Att försöka plocka en mycket rekursiv datastruktur kan överskrida det maximala rekursionsdjupet, ett RecursionError kommer att uppstå i detta fall. Du kan försiktigt höja denna gräns med sys.setrecursionlimit().

Observera att funktioner (inbyggda och användardefinierade) betas med fullt kvalificerat namn, inte med värde. [2] Detta innebär att endast funktionsnamnet betas, tillsammans med namnet på den modul och de klasser som innehåller funktionen. Varken funktionens kod eller några av dess funktionsattribut betas. Den definierande modulen måste alltså vara importerbar i unpickling-miljön, och modulen måste innehålla det namngivna objektet, annars kommer ett undantag att uppstå. [3]

På samma sätt betas klasser med fullt kvalificerat namn, så samma restriktioner i betningsmiljön gäller. Observera att ingen av klassens kod eller data betas in, så i följande exempel återställs inte klassattributet attr i unpickling-miljön:

class Foo:
    attr = 'A class attribute'

picklestring = pickle.dumps(Foo)

Dessa begränsningar är skälet till att picklbara funktioner och klasser måste definieras på den översta nivån i en modul.

På samma sätt, när klassinstanser betas, betas inte deras klass kod och data tillsammans med dem. Det är bara instansens data som syltas. Detta görs med avsikt, så att du kan åtgärda buggar i en klass eller lägga till metoder i klassen och fortfarande ladda objekt som skapades med en tidigare version av klassen. Om du planerar att ha långlivade objekt som kommer att se många versioner av en klass kan det vara värt att lägga in ett versionsnummer i objekten så att lämpliga konverteringar kan göras av klassens __setstate__()-metod.

Beizning av klassinstanser

I det här avsnittet beskriver vi de allmänna mekanismer som finns tillgängliga för att definiera, anpassa och styra hur klassinstanser ska picklas och unpicklas.

I de flesta fall behövs ingen ytterligare kod för att göra instanser plockbara. Som standard kommer pickle att hämta klassen och attributen för en instans via introspektion. När en klassinstans avplockas anropas dess __init__()-metod vanligtvis inte. Standardbeteendet skapar först en oinitialiserad instans och återställer sedan de sparade attributen. Följande kod visar en implementering av detta beteende:

def save(obj):
    return (obj.__class__, obj.__dict__)

def restore(cls, attributes):
    obj = cls.__new__(cls)
    obj.__dict__.update(attributes)
    return obj

Klasser kan ändra standardbeteendet genom att tillhandahålla en eller flera specialmetoder:

object.__getnewargs_ex__()

I protokoll 2 och nyare kan klasser som implementerar metoden __getnewargs_ex__() diktera de värden som skickas till metoden __new__() vid unpickling. Metoden måste returnera ett par (args, kwargs) där args är en tupel av positionella argument och kwargs en dictionary av namngivna argument för att konstruera objektet. Dessa kommer att skickas till __new__()-metoden vid uppplockning.

Du bör implementera denna metod om metoden __new__() i din klass kräver argument som endast innehåller nyckelord. I annat fall rekommenderas det av kompatibilitetsskäl att implementera __getnewargs__().

Ändrad i version 3.6: __getnewargs_ex__() används nu i protokoll 2 och 3.

object.__getnewargs__()

Denna metod tjänar ett liknande syfte som __getnewargs_ex__(), men stöder endast positionella argument. Den måste returnera en tupel av argument args som kommer att skickas till __new__`() metoden vid uppplockning.

__getnewargs__() kommer inte att anropas om __getnewargs_ex__() är definierad.

Ändrad i version 3.6: Före Python 3.6 anropades __getnewargs__() i stället för __getnewargs_ex__() i protokoll 2 och 3.

object.__getstate__()

Klasser kan ytterligare påverka hur deras instanser picklas genom att åsidosätta metoden __getstate__(). Den anropas och det returnerade objektet picklas som innehåll för instansen, istället för ett standardtillstånd. Det finns flera fall:

  • För en klass som inte har någon instans __dict__ och ingen __slots__ är standardtillståndet None.

  • För en klass som har en instans __dict__ och ingen __slots__, är standardtillståndet self.__dict__.

  • För en klass som har en instans __dict__ och __slots__ är standardtillståndet en tupel som består av två lexikon: self.__dict__ och en ordbok som mappar slotnamn till slotvärden. Endast slots som har ett värde ingår i den senare.

  • För en klass som har __slots__ och ingen instans __dict__, är standardtillståndet en tupel vars första post är None och vars andra post är en ordbok som mappar slotnamn till slotvärden som beskrivs i föregående punkt.

Ändrad i version 3.11: Lade till standardimplementeringen av metoden __getstate__() i klassen object.

object.__setstate__(state)

Vid unpickling, om klassen definierar __setstate__(), anropas den med det unpicklade tillståndet. I det fallet finns det inget krav på att state-objektet ska vara en dictionary. Annars måste det inlagda tillståndet vara en ordbok och dess objekt tilldelas den nya instansens ordbok.

Anteckning

Om __reduce__() returnerar ett tillstånd med värdet None vid pickling, kommer metoden __setstate__() inte att anropas vid unpickling.

Se avsnittet Hantering av Stateful-objekt för mer information om hur du använder metoderna __getstate__() och __setstate__().

Anteckning

Vid unpickling-tiden kan vissa metoder som __getattr__(), __getattribute__() eller __setattr__() anropas på instansen. Om dessa metoder är beroende av att någon intern invariant är sann, bör typen implementera __new__() för att etablera en sådan invariant, eftersom __init__() inte anropas när en instans plockas upp.

Som vi kommer att se använder pickle inte direkt de metoder som beskrivs ovan. I själva verket är dessa metoder en del av kopieringsprotokollet som implementerar specialmetoden __reduce__(). Kopieringsprotokollet ger ett enhetligt gränssnitt för att hämta de data som behövs för att plocka och kopiera objekt. [4]

Även om __reduce__() är kraftfull är det felbenäget att implementera den direkt i dina klasser. Av denna anledning bör klassutvecklare använda högnivågränssnittet (dvs. __getnewargs_ex__(), __getstate__() och __setstate__()) när det är möjligt. Vi kommer dock att visa fall där användning av __reduce__() är det enda alternativet eller leder till effektivare betning eller bådadera.

object.__reduce__()

Gränssnittet är för närvarande definierat enligt följande. Metoden __reduce__() tar inget argument och skall returnera antingen en sträng eller helst en tupel (det returnerade objektet kallas ofta för ”reduktionsvärdet”).

Om en sträng returneras ska strängen tolkas som namnet på en global variabel. Det bör vara objektets lokala namn i förhållande till dess modul; pickle-modulen söker i modulnamnrymden för att fastställa objektets modul. Detta beteende är typiskt användbart för singletons.

När en tupel returneras måste den vara mellan två och sex poster lång. Valfria objekt kan antingen utelämnas, eller så kan None anges som värde. Semantiken för varje objekt är i ordning:

  • Ett anropsbart objekt som kommer att anropas för att skapa den första versionen av objektet.

  • En tupel av argument för det anropbara objektet. En tom tupel måste anges om det anropbara objektet inte accepterar något argument.

  • Eventuellt objektets tillstånd, som kommer att skickas till objektets __setstate__()-metod enligt tidigare beskrivning. Om objektet inte har någon sådan metod måste värdet vara en dictionary och det kommer att läggas till i objektets attribut __dict__.

  • Eventuellt en iterator (och inte en sekvens) som ger successiva objekt. Dessa objekt kommer att läggas till objektet antingen med hjälp av obj.append(item) eller, i batch, med hjälp av obj.extend(list_of_items). Detta används främst för list-subklasser, men kan användas av andra klasser så länge de har append och extend metoder med lämplig signatur. (Om append() eller extend() används beror på vilken version av pickle-protokollet som används samt antalet objekt som ska läggas till, så båda måste stödjas)

  • Eventuellt en iterator (inte en sekvens) som ger successiva nyckel-värde-par. Dessa objekt kommer att lagras i objektet med obj[key] = value. Detta används främst för dictionary-subklasser, men kan användas av andra klasser så länge de implementerar __setitem__().

  • Eventuellt en anropbar funktion med signaturen (obj, state). Denna anropsbarhet tillåter användaren att programmatiskt kontrollera tillståndsuppdateringsbeteendet för ett specifikt objekt, istället för att använda obj statiska __setstate__() metod. Om inte None, kommer denna anropsbarhet att ha prioritet över obj __setstate__().

    Tillagd i version 3.8: Det valfria sjätte tuple-objektet, (obj, state), lades till.

object.__reduce_ex__(protocol)

Alternativt kan en __reduce_ex__()-metod definieras. Den enda skillnaden är att denna metod ska ta ett enda heltalsargument, protokollversionen. När den definieras kommer pickle att föredra den framför __reduce__()-metoden. Dessutom blir __reduce__() automatiskt en synonym för den utökade versionen. Huvudanvändningen för denna metod är att tillhandahålla bakåtkompatibla reduce-värden för äldre Python-versioner.

Persistens för externa objekt

För att underlätta objektets beständighet stöder modulen pickle begreppet referens till ett objekt utanför den betade dataströmmen. Sådana objekt refereras till med ett beständigt ID, som antingen bör vara en sträng med alfanumeriska tecken (för protokoll 0) [5] eller bara ett godtyckligt objekt (för alla nyare protokoll).

Upplösningen av sådana persistenta ID:n definieras inte av modulen pickle; den delegerar denna upplösning till de användardefinierade metoderna på picklern och unpickllern, persistent_id() respektive persistent_load().

För att plocka objekt som har ett externt beständigt ID måste plockaren ha en egen persistent_id()-metod som tar ett objekt som argument och returnerar antingen None eller det beständiga ID:t för objektet. När None returneras betar betaren helt enkelt objektet som normalt. När en persistent ID-sträng returneras kommer picklern att pickla det objektet, tillsammans med en markör så att unpickllern känner igen det som ett persistent ID.

För att unpickla externa objekt måste unpicklern ha en anpassad persistent_load()-metod som tar ett persistent ID-objekt och returnerar det refererade objektet.

Här följer ett omfattande exempel som visar hur persistent ID kan användas för att plocka externa objekt genom referens.

# Enkelt exempel som visar hur persistent ID kan användas för att plocka
# externa objekt genom referens.

import pickle
import sqlite3
from collections import namedtuple

# Simple class representing a record in our database.
MemoRecord = namedtuple("MemoRecord", "key, task")

class DBPickler(pickle.Pickler):

    def persistent_id(self, obj):
        # Instead of pickling MemoRecord as a regular class instance, we emit a
        # persistent ID.
        if isinstance(obj, MemoRecord):
            # Here, our persistent ID is simply a tuple, containing a tag and a
            # key, which refers to a specific record in the database.
            return ("MemoRecord", obj.key)
        else:
            # If obj does not have a persistent ID, return None. This means obj
            # needs to be pickled as usual.
            return None


class DBUnpickler(pickle.Unpickler):

    def __init__(self, file, connection):
        super().__init__(file)
        self.connection = connection

    def persistent_load(self, pid):
        # This method is invoked whenever a persistent ID is encountered.
        # Here, pid is the tuple returned by DBPickler.
        cursor = self.connection.cursor()
        type_tag, key_id = pid
        if type_tag == "MemoRecord":
            # Fetch the referenced record from the database and return it.
            cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
            key, task = cursor.fetchone()
            return MemoRecord(key, task)
        else:
            # Always raises an error if you cannot return the correct object.
            # Otherwise, the unpickler will think None is the object referenced
            # by the persistent ID.
            raise pickle.UnpicklingError("unsupported persistent object")


def main():
    import io
    import pprint

    # Initialize and populate our database.
    conn = sqlite3.connect(":memory:")
    cursor = conn.cursor()
    cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
    tasks = (
        'give food to fish',
        'prepare group meeting',
        'fight with a zebra',
        )
    for task in tasks:
        cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))

    # Fetch the records to be pickled.
    cursor.execute("SELECT * FROM memos")
    memos = [MemoRecord(key, task) for key, task in cursor]
    # Save the records using our custom DBPickler.
    file = io.BytesIO()
    DBPickler(file).dump(memos)

    print("Pickled records:")
    pprint.pprint(memos)

    # Update a record, just for good measure.
    cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")

    # Load the records from the pickle data stream.
    file.seek(0)
    memos = DBUnpickler(file, conn).load()

    print("Unpickled records:")
    pprint.pprint(memos)


if __name__ == '__main__':
    main()

Expeditionsbord

Om man vill anpassa betningen av vissa klasser utan att störa någon annan kod som är beroende av betningen, kan man skapa en betare med en privat dispatch-tabell.

Den globala dispatch-tabellen som hanteras av modulen copyreg är tillgänglig som copyreg.dispatch_table. Därför kan man välja att använda en modifierad kopia av copyreg.dispatch_table som en privat dispatch-tabell.

Till exempel

f = io.BytesIO()
p = pickle.Pickler(f)
p.dispatch_table = copyreg.dispatch_table.copy()
p.dispatch_table[SomeClass] = reduce_SomeClass

skapar en instans av pickle.Pickler med en privat dispatch-tabell som hanterar klassen SomeClass speciellt. Alternativt kan koden

class MyPickler(pickle.Pickler):
    dispatch_table = copyreg.dispatch_table.copy()
    dispatch_table[SomeClass] = reduce_SomeClass
f = io.BytesIO()
p = MyPickler(f)

gör samma sak men alla instanser av MyPickler kommer som standard att dela den privata dispatch-tabellen. Å andra sidan, koden

copyreg.pickle(SomeClass, reduce_SomeClass)
f = io.BytesIO()
p = pickle.Pickler(f)

ändrar den globala fördelningstabellen som delas av alla användare av modulen copyreg.

Hantering av Stateful-objekt

Här är ett exempel som visar hur man ändrar pickling-beteendet för en klass. Klassen TextReader nedan öppnar en textfil och returnerar radnummer och radinnehåll varje gång dess metod readline() anropas. Om en TextReader-instans är picklad sparas alla attribut utom objektmedlemmen file. När instansen avpicklas öppnas filen igen och läsningen återupptas från den senaste platsen. Metoderna __setstate__() och __getstate__() används för att implementera detta beteende.

class TextReader:
    """Print and number lines in a text file."""

    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename)
        self.lineno = 0

    def readline(self):
        self.lineno += 1
        line = self.file.readline()
        if not line:
            return None
        if line.endswith('\n'):
            line = line[:-1]
        return "%i: %s" % (self.lineno, line)

    def __getstate__(self):
        # Copy the object's state from self.__dict__ which contains
        # all our instance attributes. Always use the dict.copy()
        # method to avoid modifying the original state.
        state = self.__dict__.copy()
        # Remove the unpicklable entries.
        del state['file']
        return state

    def __setstate__(self, state):
        # Restore instance attributes (i.e., filename and lineno).
        self.__dict__.update(state)
        # Restore the previously opened file's state. To do so, we need to
        # reopen it and read from it until the line count is restored.
        file = open(self.filename)
        for _ in range(self.lineno):
            file.readline()
        # Finally, save the file.
        self.file = file

Ett exempel på användning kan vara ungefär så här:

>>> reader = TextReader("hello.txt")
>>> reader.readline()
'1: Hello world!'
>>> reader.readline()
'2: I am line number two.'
>>> new_reader = pickle.loads(pickle.dumps(reader))
>>> new_reader.readline()
'3: Goodbye!'

Anpassad reduktion för typer, funktioner och andra objekt

Tillagd i version 3.8.

Ibland kanske dispatch_table inte är tillräckligt flexibel. I synnerhet kanske vi vill anpassa betningen baserat på ett annat kriterium än objektets typ, eller så kanske vi vill anpassa betningen av funktioner och klasser.

För dessa fall är det möjligt att subklassa från Pickler-klassen och implementera en reducer_override()-metod. Denna metod kan returnera en godtycklig reduktionstupel (se __reduce__()). Den kan alternativt returnera NotImplemented för att återgå till det traditionella beteendet.

Om både dispatch_table och reducer_override() är definierade, så har metoden reducer_override() prioritet.

Anteckning

Av prestandaskäl får reducer_override() inte anropas för följande objekt: None, True, False och exakta instanser av int, float, bytes, str, dict, set, frozenset, list och tuple.

Här är ett enkelt exempel där vi tillåter betning och rekonstruktion av en given klass:

import io
import pickle

class MyClass:
    my_attribute = 1

class MyPickler(pickle.Pickler):
    def reducer_override(self, obj):
        """Custom reducer for MyClass."""
        if getattr(obj, "__name__", None) == "MyClass":
            return type, (obj.__name__, obj.__bases__,
                          {'my_attribute': obj.my_attribute})
        else:
            # For any other object, fallback to usual reduction
            return NotImplemented

f = io.BytesIO()
p = MyPickler(f)
p.dump(MyClass)

del MyClass

unpickled_class = pickle.loads(f.getvalue())

assert isinstance(unpickled_class, type)
assert unpickled_class.__name__ == "MyClass"
assert unpickled_class.my_attribute == 1

Buffertar utanför bandet

Tillagd i version 3.8.

I vissa sammanhang används modulen pickle för att överföra stora mängder data. Därför kan det vara viktigt att minimera antalet minneskopior för att bevara prestanda och resursförbrukning. Normal användning av modulen pickle, som omvandlar en grafliknande struktur av objekt till en sekventiell ström av bytes, innebär dock i sig att data kopieras till och från pickle-strömmen.

Denna begränsning kan undvikas om både provider (implementationen av de objekttyper som ska överföras) och consumer (implementationen av kommunikationssystemet) stöder de överföringsmöjligheter utanför bandet som tillhandahålls av pickle protocol 5 och högre.

Leverantörs-API

De stora dataobjekt som ska betas måste implementera en __reduce_ex__()-metod som är specialiserad för protokoll 5 och högre, som returnerar en PickleBuffer-instans (i stället för t.ex. ett bytes-objekt) för alla stora data.

Ett PickleBuffer-objekt signalerar att den underliggande bufferten är kvalificerad för dataöverföring utanför bandet. Dessa objekt förblir kompatibla med normal användning av modulen pickle. Men konsumenter kan också välja att tala om för pickle att de kommer att hantera dessa buffertar själva.

API för konsumenter

Ett kommunikationssystem kan möjliggöra anpassad hantering av PickleBuffer-objekt som genereras vid serialisering av en objektgraf.

På sändningssidan måste den skicka ett buffer_callback-argument till Pickler (eller till dump() eller dumps()-funktionen), som kommer att anropas med varje PickleBuffer som genereras när objektgrafen plockas. Buffertar som ackumuleras av buffer_callback kommer inte att få sina data kopierade till pickle-strömmen, endast en billig markör kommer att infogas.

På mottagarsidan måste den skicka ett buffers-argument till Unpickler (eller till funktionen load() eller loads()), som är en iterabel av de buffertar som skickades till buffer_callback. Denna iterabel bör producera buffertar i samma ordning som de skickades till buffer_callback. Dessa buffertar kommer att tillhandahålla de data som förväntas av rekonstruktörerna av de objekt vars betning producerade de ursprungliga PickleBuffer-objekten.

Mellan sändar- och mottagarsidan är kommunikationssystemet fritt att implementera sin egen överföringsmekanism för buffertar utanför bandet. Potentiella optimeringar inkluderar användning av delat minne eller datatypberoende komprimering.

Exempel

Här är ett trivialt exempel där vi implementerar en bytearray-underklass som kan delta i buffer pickling utanför bandet:

class ZeroCopyByteArray(bytearray):

    def __reduce_ex__(self, protocol):
        if protocol >= 5:
            return type(self)._reconstruct, (PickleBuffer(self),), None
        else:
            # PickleBuffer is forbidden with pickle protocols <= 4.
            return type(self)._reconstruct, (bytearray(self),)

    @classmethod
    def _reconstruct(cls, obj):
        with memoryview(obj) as m:
            # Get a handle over the original buffer object
            obj = m.obj
            if type(obj) is cls:
                # Original buffer object is a ZeroCopyByteArray, return it
                # as-is.
                return obj
            else:
                return cls(obj)

Rekonstruktören (klassmetoden _reconstruct) returnerar buffertens tillhandahållande objekt om det har rätt typ. Detta är ett enkelt sätt att simulera nollkopieringsbeteende i detta leksaksexempel.

På konsumentsidan kan vi plocka upp dessa objekt på vanligt sätt, vilket när det är oserialiserat ger oss en kopia av det ursprungliga objektet:

b = ZeroCopyByteArray(b"abc")
data = pickle.dumps(b, protocol=5)
new_b = pickle.loads(data)
print(b == new_b)  # True
print(b is new_b)  # False: a copy was made

Men om vi skickar en buffer_callback och sedan ger tillbaka de ackumulerade buffertarna när vi unserialiserar, kan vi få tillbaka det ursprungliga objektet:

b = ZeroCopyByteArray(b"abc")
buffers = []
data = pickle.dumps(b, protocol=5, buffer_callback=buffers.append)
new_b = pickle.loads(data, buffers=buffers)
print(b == new_b)  # True
print(b is new_b)  # True: no copy was made

Detta exempel begränsas av det faktum att bytearray allokerar sitt eget minne: du kan inte skapa en bytearray-instans som backas upp av ett annat objekts minne. Tredjepartsdatatyper som NumPy-arrays har dock inte denna begränsning och tillåter användning av zero-copy pickling (eller att göra så få kopior som möjligt) vid överföring mellan olika processer eller system.

Se även

PEP 574 – Pickle protokoll 5 med data utanför bandet

Begränsning av globaler

Som standard importerar unpickling alla klasser eller funktioner som den hittar i pickle-data. För många applikationer är detta beteende oacceptabelt eftersom det tillåter unpickling att importera och anropa godtycklig kod. Tänk bara på vad den här handgjorda pickle-dataströmmen gör när den laddas:

>>> import pickle
>>> pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
hello world
0

I det här exemplet importerar unpickler funktionen os.system() och använder sedan strängargumentet ”echo hello world”. Även om detta exempel är ofarligt är det inte svårt att föreställa sig ett exempel som kan skada ditt system.

Av denna anledning kanske du vill kontrollera vad som blir unpickled genom att anpassa Unpickler.find_class(). Till skillnad från vad namnet antyder anropas Unpickler.find_class() när en global (dvs. en klass eller en funktion) begärs. Det är alltså möjligt att antingen helt förbjuda globaler eller begränsa dem till en säker delmängd.

Här är ett exempel på en unpickler som tillåter att endast ett fåtal säkra klasser från modulen builtins laddas:

import builtins
import io
import pickle

safe_builtins = {
    'range',
    'complex',
    'set',
    'frozenset',
    'slice',
}

class RestrictedUnpickler(pickle.Unpickler):

    def find_class(self, module, name):
        # Only allow safe classes from builtins.
        if module == "builtins" and name in safe_builtins:
            return getattr(builtins, name)
        # Forbid everything else.
        raise pickle.UnpicklingError("global '%s.%s' is forbidden" %
                                     (module, name))

def restricted_loads(s):
    """Helper function analogous to pickle.loads()."""
    return RestrictedUnpickler(io.BytesIO(s)).load()

Ett exempel på användning av vår unpickler som fungerar som avsett:

>>> restricted_loads(pickle.dumps([1, 2, range(15)]))
[1, 2, range(0, 15)]
>>> restricted_loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
Traceback (most recent call last):
  ...
pickle.UnpicklingError: global 'os.system' is forbidden
>>> restricted_loads(b'cbuiltins\neval\n'
...                  b'(S\'getattr(__import__("os"), "system")'
...                  b'("echo hello world")\'\ntR.')
Traceback (most recent call last):
  ...
pickle.UnpicklingError: global 'builtins.eval' is forbidden

Som våra exempel visar måste du vara försiktig med vad du tillåter att bli unpickled. Om säkerhet är ett problem kan du därför överväga alternativ som marshalling API i xmlrpc.client eller tredjepartslösningar.

Prestanda

De senaste versionerna av pickle-protokollet (från protokoll 2 och uppåt) har effektiva binära kodningar för flera vanliga funktioner och inbyggda typer. Dessutom har modulen pickle en transparent optimerare skriven i C.

Exempel

För den enklaste koden, använd funktionerna dump() och load().

import pickle

# An arbitrary collection of objects supported by pickle.
data = {
    'a': [1, 2.0, 3+4j],
    'b': ("character string", b"byte string"),
    'c': {None, True, False}
}

with open('data.pickle', 'wb') as f:
    # Pickle the 'data' dictionary using the highest protocol available.
    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

I följande exempel läses den resulterande picklade datan.

import pickle

with open('data.pickle', 'rb') as f:
    # The protocol version used is detected automatically, so we do not
    # have to specify it.
    data = pickle.load(f)

Kommandoradsgränssnitt

Modulen pickle kan anropas som ett skript från kommandoraden och visar innehållet i pickle-filerna. Men när pickle-filen som du vill undersöka kommer från en opålitlig källa är -m pickletools ett säkrare alternativ eftersom den inte exekverar pickle-bytekod, se pickletools CLI usage.

python -m pickle pickle_file [pickle_file ...]

Följande alternativ accepteras:

pickle_file

En pickle-fil som ska läsas, eller - för att ange läsning från standardinmatning.

Se även

Modul copyreg

Registrering av Pickle-gränssnittskonstruktörer för tilläggstyper.

Modul pickletools

Verktyg för att arbeta med och analysera betade data.

Modul shelve

Indexerade databaser med objekt; använder pickle.

Modul copy

Kopiering av grunda och djupa objekt.

Modul marshal

Högpresterande serialisering av inbyggda typer.

Fotnoter