7. Inmatning och utmatning

Det finns flera sätt att presentera utdata från ett program; data kan skrivas ut i en läsbar form eller skrivas till en fil för framtida användning. I detta kapitel diskuteras några av dessa möjligheter.

7.1. Mer avancerad utmatningsformatering

Hittills har vi stött på två sätt att skriva värden: uttryckssatser och funktionen print(). (Ett tredje sätt är att använda metoden write() för filobjekt; standardutdatafilen kan refereras till som sys.stdout. Se biblioteksreferensen för mer information om detta)

Ofta vill du ha mer kontroll över formateringen av dina utdata än att bara skriva ut mellanslagsseparerade värden. Det finns flera sätt att formatera utdata.

  • För att använda formaterade stränglitteraler, börja en sträng med f eller F före det inledande citattecknet eller trippelcitationstecknet. Inuti denna sträng kan du skriva ett Python-uttryck mellan tecknen { och } som kan referera till variabler eller bokstavliga värden.

    >>> year = 2016
    >>> event = 'Referendum'
    >>> f'Results of the {year} {event}'
    'Results of the 2016 Referendum'
    
  • Metoden str.format() för strängar kräver mer manuellt arbete. Du kommer fortfarande att använda { och } för att markera var en variabel ska ersättas och kan ge detaljerade formateringsdirektiv, men du måste också tillhandahålla den information som ska formateras. I följande kodblock finns två exempel på hur man formaterar variabler:

    >>> yes_votes = 42_572_654
    >>> total_votes = 85_705_149
    >>> percentage = yes_votes / total_votes
    >>> '{:-9} YES votes  {:2.2%}'.format(yes_votes, percentage)
    ' 42572654 YES votes  49.67%'
    

    Lägg märke till hur yes_votes fylls i med mellanslag och ett negativt tecken endast för negativa tal. I exemplet skrivs även procent ut multiplicerat med 100, med 2 decimaler och följt av ett procenttecken (se Formatspecifikation Mini-språk för detaljer).

  • Slutligen kan du göra all stränghantering själv genom att använda strängskärning och konkatenering för att skapa vilken layout du än kan tänka dig. Strängtypen har några metoder som utför användbara operationer för att fylla ut strängar till en given kolumnbredd.

Om du inte behöver några avancerade utdata utan bara vill ha en snabb visning av några variabler för felsökning kan du konvertera valfritt värde till en sträng med funktionerna repr() eller str().

Funktionen str() är tänkt att returnera representationer av värden som är någorlunda läsbara för människor, medan repr() är tänkt att generera representationer som kan läsas av tolken (eller tvinga fram ett SyntaxError om det inte finns någon motsvarande syntax). För objekt som inte har en särskild representation för mänsklig konsumtion, kommer str() att returnera samma värde som repr(). Många värden, t.ex. tal eller strukturer som listor och ordböcker, har samma representation med båda funktionerna. Strängar, i synnerhet, har två olika representationer.

Några exempel:

>>> s = 'Hello, world.'
>>> str(s)
'Hello, world.'
>>> repr(s)
"'Hello, world.'"
>>> str(1/7)
'0.14285714285714285'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'The value of x is ' + repr(x) + ', and y is ' + repr(y) + '...'
>>> print(s)
The value of x is 32.5, and y is 40000...
>>> # The repr() of a string adds string quotes and backslashes:
>>> hello = 'hello, world\n'
>>> hellos = repr(hello)
>>> print(hellos)
'hello, world\n'
>>> # The argument to repr() may be any Python object:
>>> repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"

Modulen string innehåller stöd för en enkel mallbaserad metod baserad på reguljära uttryck, via string.Template. Detta erbjuder ännu ett sätt att ersätta värden i strängar, genom att använda platshållare som $x och ersätta dem med värden från en ordlista. Denna syntax är enkel att använda, men erbjuder mycket mindre kontroll över formateringen.

7.1.1. Formaterade strängbokstäver

Formaterade stränglitteraler (även kallade f-strängar i korthet) låter dig inkludera värdet av Python-uttryck inuti en sträng genom att prefixera strängen med f eller F och skriva uttryck som {expression}.

En valfri formatspecifikator kan följa efter uttrycket. Detta ger större kontroll över hur värdet formateras. I följande exempel avrundas pi till tre ställen efter decimalen:

>>> import math
>>> print(f'The value of pi is approximately {math.pi:.3f}.')
The value of pi is approximately 3.142.

Om du skickar ett heltal efter ':' blir fältet minst ett antal tecken brett. Detta är användbart för att få kolumner att rada upp sig.

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
...     print(f'{name:10} ==> {phone:10d}')
...
Sjoerd     ==>       4127
Jack       ==>       4098
Dcab       ==>       7678

Andra modifierare kan användas för att konvertera värdet innan det formateras. '!a' tillämpar ascii(), '!s' tillämpar str(), och '!r' tillämpar repr():

>>> animals = 'eels'
>>> print(f'My hovercraft is full of {animals}.')
My hovercraft is full of eels.
>>> print(f'My hovercraft is full of {animals!r}.')
My hovercraft is full of 'eels'.

Specificeraren = kan användas för att expandera ett uttryck till texten i uttrycket, ett likhetstecken och sedan representationen av det utvärderade uttrycket:

>>> bugs = 'roaches'
>>> count = 13
>>> area = 'living room'
>>> print(f'Debugging {bugs=} {count=} {area=}')
Debugging bugs='roaches' count=13 area='living room'

Se self-documenting expressions för mer information om = specifikatorn. För en referens om dessa formatspecifikationer, se referensguiden för Formatspecifikation Mini-språk.

7.1.2. Metoden String format()

Grundläggande användning av metoden str.format() ser ut så här:

>>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
We are the knights who say "Ni!"

Parenteserna och tecknen inom dem (som kallas formatfält) ersätts med de objekt som skickas till metoden str.format(). Ett tal inom parentes kan användas för att referera till positionen för det objekt som skickas till metoden str.format().

>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam

Om nyckelordsargument används i metoden str.format() refereras till deras värden genom att använda argumentets namn.

>>> print('This {food} is {adjective}.'.format(
...       food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.

Positions- och nyckelordsargument kan kombineras godtyckligt:

>>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
...                                                    other='Georg'))
The story of Bill, Manfred, and Georg.

Om du har en riktigt lång formatsträng som du inte vill dela upp, skulle det vara bra om du kunde referera till variablerna som ska formateras med namn istället för med position. Detta kan göras genom att helt enkelt skicka dict och använda hakparenteser '[]' för att komma åt nycklarna.

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
...       'Dcab: {0[Dcab]:d}'.format(table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Detta kan också göras genom att skicka ordboken table som nyckelordsargument med notation **.

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print('Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table))
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Detta är särskilt användbart i kombination med den inbyggda funktionen vars(), som returnerar en ordbok som innehåller alla lokala variabler:

>>> table = {k: str(v) for k, v in vars().items()}
>>> message = " ".join([f'{k}: ' + '{' + k +'};' for k in table.keys()])
>>> print(message.format(**table))
__name__: __main__; __doc__: None; __package__: None; __loader__: ...

Som exempel kan nämnas att följande rader ger en välordnad uppsättning kolumner med heltal och deras kvadrater och kuber:

>>> for x in range(1, 11):
...     print('{0:2d} {1:3d} {2:4d}'.format(x, x*x, x*x*x))
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

För en fullständig översikt över strängformatering med str.format(), se Format String Syntax.

7.1.3. Manuell strängformatering

Här är samma tabell med kvadrater och kuber, formaterad manuellt:

>>> for x in range(1, 11):
...     print(repr(x).rjust(2), repr(x*x).rjust(3), end=' ')
...     # Note use of 'end' on previous line
...     print(repr(x*x*x).rjust(4))
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

(Observera att ett mellanslag mellan varje kolumn lades till på grund av hur print() fungerar: den lägger alltid till mellanslag mellan sina argument)

Metoden str.rjust() för strängobjekt högerjusterar en sträng i ett fält med en given bredd genom att fylla ut den med mellanslag till vänster. Det finns liknande metoder str.ljust() och str.center(). Dessa metoder skriver inte något, de returnerar bara en ny sträng. Om indatasträngen är för lång trunkerar de den inte utan returnerar den oförändrad; detta kommer att röra till din kolumnlayout men det är vanligtvis bättre än alternativet, vilket skulle vara att ljuga om ett värde. (Om du verkligen vill ha trunkering kan du alltid lägga till en slice-operation, som i x.ljust(n)[:n])

Det finns en annan metod, str.zfill(), som fyller ut en numerisk sträng till vänster med nollor. Den förstår sig på plus- och minustecken:

>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

7.1.4. Gammal strängformatering

%-operatorn (modulo) kan också användas för strängformatering. Givet format % values (där format är en sträng), ersätts % konverteringsspecifikationer i format med noll eller flera element i värden. Denna operation är allmänt känd som stränginterpolation. Till exempel:

>>> import math
>>> print('The value of pi is approximately %5.3f.' % math.pi)
The value of pi is approximately 3.142.

Mer information finns i avsnittet strängformatering i stil med printf.

7.2. Läsa och skriva filer

open() returnerar ett file object, och används oftast med två positionella argument och ett nyckelordsargument: open(filename, mode, encoding=None)

>>> f = open('workfile', 'w', encoding="utf-8")

Det första argumentet är en sträng som innehåller filnamnet. Det andra argumentet är en annan sträng som innehåller några tecken som beskriver hur filen ska användas. mode kan vara 'r' om filen bara ska läsas, 'w' om den bara ska skrivas (en befintlig fil med samma namn raderas), och 'a' öppnar filen för tillägg; all data som skrivs till filen läggs automatiskt till i slutet. 'r+' öppnar filen för både läsning och skrivning. Argumentet mode är valfritt; 'r' kommer att antas om det utelämnas.

Normalt öppnas filer i text mode, vilket innebär att du läser och skriver strängar från och till filen, som är kodade i en specifik encoding. Om encoding inte anges är standardinställningen plattformsberoende (se open()). Eftersom UTF-8 är den moderna de-facto-standarden rekommenderas encoding="utf-8" om du inte vet att du behöver använda en annan kodning. Genom att lägga till 'b' till mode öppnas filen i binary mode. Data i binärt läge läses och skrivs som bytes-objekt. Du kan inte ange kodning när du öppnar en fil i binärt läge.

I textläge är standardinställningen vid läsning att konvertera plattformsspecifika radavslutningar (\n på Unix, \r\n på Windows) till bara \n. När du skriver i textläge är standardinställningen att konvertera förekomster av \n tillbaka till plattformsspecifika radavslut. Denna bakomliggande modifiering av fildata är bra för textfiler, men kommer att korrumpera binära data som i JPEG- eller EXE-filer. Var mycket försiktig med att använda binärt läge när du läser och skriver sådana filer.

Det är god praxis att använda nyckelordet with när man hanterar filobjekt. Fördelen är att filen stängs på rätt sätt när dess svit är klar, även om ett undantag uppstår vid något tillfälle. Att använda with är också mycket kortare än att skriva motsvarande try- finally-block:

>>> with open('workfile', encoding="utf-8") as f:
...     read_data = f.read()

>>> # We can check that the file has been automatically closed.
>>> f.closed
True

Om du inte använder nyckelordet with bör du anropa f.close() för att stänga filen och omedelbart frigöra alla systemresurser som används av den.

Varning

Anrop av f.write() utan att använda nyckelordet with eller anrop av f.close() kan resultera i att argumenten i f.write() inte skrivs ut fullständigt till disken, även om programmet avslutas framgångsrikt.

När ett filobjekt har stängts, antingen genom en with-sats eller genom att anropa f.close(), kommer försök att använda filobjektet automatiskt att misslyckas.

>>> f.close()
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

7.2.1. Metoder för filobjekt

I resten av exemplen i det här avsnittet förutsätts att ett filobjekt med namnet f redan har skapats.

För att läsa en fils innehåll, anropa f.read(size), som läser en viss mängd data och returnerar den som en sträng (i textläge) eller ett bytesobjekt (i binärt läge). size är ett valfritt numeriskt argument. När size utelämnas eller är negativt kommer hela filens innehåll att läsas och returneras; det är ditt problem om filen är dubbelt så stor som din maskins minne. Annars läses och returneras högst size tecken (i textläge) eller size byte (i binärt läge). Om slutet av filen har nåtts kommer f.read() att returnera en tom sträng ('').

>>> f.read()
'This is the entire file.\n'
>>> f.read()
''

f.readline() läser en enda rad från filen; ett tecken för ny rad (\n) lämnas i slutet av strängen och utelämnas endast på den sista raden i filen om filen inte slutar med en ny rad. Detta gör returvärdet otvetydigt; om f.readline() returnerar en tom sträng har slutet på filen nåtts, medan en tom rad representeras av '\n', en sträng som bara innehåller en enda ny rad.

>>> f.readline()
'This is the first line of the file.\n'
>>> f.readline()
'Second line of the file\n'
>>> f.readline()
''

För att läsa rader från en fil kan du loopa över filobjektet. Detta är minnessnålt, snabbt och leder till enkel kod:

>>> for line in f:
...     print(line, end='')
...
This is the first line of the file.
Second line of the file

Om du vill läsa alla rader i en fil i en lista kan du också använda list(f) eller f.readlines().

f.write(string) skriver innehållet i string till filen och returnerar antalet tecken som skrivits.

>>> f.write('This is a test\n')
15

Andra typer av objekt måste konverteras - antingen till en sträng (i textläge) eller till ett bytesobjekt (i binärt läge) - innan de skrivs:

>>> value = ('the answer', 42)
>>> s = str(value)  # convert the tuple to string
>>> f.write(s)
18

f.tell() returnerar ett heltal som anger filobjektets aktuella position i filen representerat som antal byte från början av filen i binärläge och ett opakt tal i textläge.

Om du vill ändra filobjektets position använder du f.seek(offset, whence). Positionen beräknas genom att lägga till offset till en referenspunkt; referenspunkten väljs med argumentet whence. Ett whence-värde på 0 mäter från början av filen, 1 använder den aktuella filpositionen och 2 använder slutet av filen som referenspunkt. whence kan utelämnas och har standardvärdet 0, vilket innebär att filens början används som referenspunkt.

>>> f = open('workfile', 'rb+')
>>> f.write(b'0123456789abcdef')
16
>>> f.seek(5)      # Go to the 6th byte in the file
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2)  # Go to the 3rd byte before the end
13
>>> f.read(1)
b'd'

I textfiler (som öppnas utan b i lägessträngen) tillåts endast sökningar relativt början av filen (undantaget är sökning till slutet av filen med seek(0, 2)) och de enda giltiga offset-värdena är de som returneras från f.tell(), eller noll. Alla andra offset-värden ger ett odefinierat beteende.

Filobjekt har några ytterligare metoder, t.ex. isatty() och truncate() som används mindre ofta; se Library Reference för en fullständig guide till filobjekt.

7.2.2. Spara strukturerad data med json

Strängar kan enkelt skrivas till och läsas från en fil. Siffror kräver lite mer ansträngning, eftersom metoden read() bara returnerar strängar, som måste skickas till en funktion som int(), som tar en sträng som '123' och returnerar dess numeriska värde 123. När du vill spara mer komplexa datatyper som nästlade listor och lexikon blir det komplicerat att parsa och serialisera för hand.

I stället för att användarna ständigt måste skriva och felsöka kod för att spara komplicerade datatyper i filer, kan du med Python använda det populära datautbytesformatet JSON (JavaScript Object Notation). Standardmodulen som kallas json kan ta Python-datahierarkier och konvertera dem till strängrepresentationer; denna process kallas serializing. Att rekonstruera data från strängrepresentationen kallas deserializing. Mellan serialisering och deserialisering kan strängen som representerar objektet ha lagrats i en fil eller data, eller skickats över en nätverksanslutning till någon avlägsen maskin.

Anteckning

JSON-formatet används ofta av moderna applikationer för att möjliggöra datautbyte. Många programmerare är redan bekanta med det, vilket gör det till ett bra val för interoperabilitet.

Om du har ett objekt x kan du visa dess JSON-strängrepresentation med en enkel kodrad:

>>> import json
>>> x = [1, 'simple', 'list']
>>> json.dumps(x)
'[1, "simple", "list"]'

En annan variant av funktionen dumps(), som kallas dump(), serialiserar helt enkelt objektet till en textfil. Så om f är ett text file-objekt som är öppet för skrivning kan vi göra så här:

json.dump(x, f)

För att avkoda objektet igen, om f är ett binary file- eller textfil-objekt som har öppnats för läsning:

x = json.load(f)

Anteckning

JSON-filer måste vara kodade i UTF-8. Använd encoding="utf-8" när du öppnar JSON-filen som en textfil för både läsning och skrivning.

Denna enkla serialiseringsteknik kan hantera listor och ordböcker, men serialisering av godtyckliga klassinstanser i JSON kräver lite extra ansträngning. Referensen för modulen json innehåller en förklaring av detta.

Se även

pickle - pickle-modulen

I motsats till JSON är pickle ett protokoll som tillåter serialisering av godtyckligt komplexa Python-objekt. Som sådant är det specifikt för Python och kan inte användas för att kommunicera med applikationer skrivna på andra språk. Det är också osäkert som standard: deserialisering av pickle-data som kommer från en icke betrodd källa kan exekvera godtycklig kod om data har skapats av en skicklig angripare.