Previous Page

Up One Level

Next Page

Python Tutorial

Contents

Index

Vorige: 6. Modules Omhoog: Python Tutorial Volgende: 8. Fouten en exceptions
Onderdelen
  • 7.1 Elegantere opmaak van uitvoer
  • 7.2 Lezen uit en schrijven naar bestanden
    • 7.2.1 Methods van file-objecten
    • 7.2.2 De module pickle


7. In- en uitvoer

Er zijn diverse manieren om de uitvoer van een programma weer te geven; gegevens kunnen in een leesbare vorm worden afgedrukt, of naar een bestand geschreven worden voor toekomstig gebruik. In dit hoofdstuk worden enkele mogelijkheden besproken.


7.1 Elegantere opmaak van uitvoer

Tot dusver hebben we twee manieren geleerd om waarden af te drukken: expressiestatements en het print-statement. (Een derde manier is om aan stdout te refereren als een bestand met de naam sys.stdout, en om daarop de write() method van file-objecten los te laten. Zie de Library Reference voor meer informatie.)

Vaak wil je meer controle over de opmaak van je uitvoer dan je krijgt door het afdrukken van door spaties gescheiden waarden. Je kunt je uitvoer op twee manieren opmaken. Bij de eerste methode doe je alle string handling zelf; met behulp van string slicing en concatenatie kun je iedere voorstelbare lay-out creëren. De standaardmodule string bevat een aantal nuttige operaties voor het aanvullen (padding) van strings tot aan een gegeven kolombreedte; we zullen deze operaties straks bespreken. Bij de twee methode gebruik je de operator % met een string als linkerargument. De %-operator interpreteert zijn linkerargument op een wijze die veel wegheeft van de sprintf()-operatie in C, nl. als een opmaakstring die toegepast moet worden op het rechterargument; het resultaat van deze opmaakoperatie wordt teruggegeven als een string.

Uiteraard blijft dan de vraag over “hoe converteer je waardes naar strings?” Gelukkig beschikt Python over manieren om iedere mogelijke waarde te converteren naar een string: nl. door deze aan te bieden aan de repr() of str() functies. Omgekeerde enkele aanhalingstekens (``) hebben hetzelfde effect als repr(), maar het wordt afgeraden deze te gebruiken.

De functie str() is bedoeld voor het teruggeven van een weergave van de waarde die redelijk goed leesbaar is voor mensen, terwijl repr() de bedoeling heeft een weergave te creëren die gelezen kan worden door de interpreter. Voor objecten die geen specifieke weergave voor menselijk gebruik hebben, geeft str() dezelfde waarde terug als repr(). Veel waarden, zoals getallen, of structuren zoals lists en dictionaries, gebruiken voor beide functies dezelfde weergave. Met name strings en floating point getallen hebben twee verschillende weergaves.

Enkele voorbeelden:

>>> s = 'Hallo, wereld.'
>>> str(s)
'Hallo, wereld.'
>>> repr(s)
"'Hallo, wereld.'"
>>> str(0.1)
'0.1'
>>> repr(0.1)
'0.10000000000000001'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'De waarde van x is ' + repr(x) + ', en y is ' + repr(y) + '...'
>>> print s
De waarde van x is 32.5, en y is 40000...
>>> # De repr() van een string voegt aanhalingstekens en backslashes toe:
... hallo = 'hallo, wereld\n'
>>> hallos = repr(hallo)
>>> print hallos
'hallo, wereld\n'
>>> # Ieder Python-object kan als argument aan repr() meegegeven worden:
... repr((x, y, ('spam', 'eggs')))
"(32.5, 40000, ('spam', 'eggs'))"
>>> # omgekeerde aanhalingstekens zijn handig in interactieve sessies:
... `x, y, ('spam', 'eggs')`
"(32.5, 40000, ('spam', 'eggs'))"

Hier volgen twee manieren om een tabel met kwadraten en derdemachten af te drukken:

>>> for x in range(1, 11):
...     print repr(x).rjust(2), repr(x*x).rjust(3),
...     # Let op de afsluitende komma op de vorige regel
...     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
>>> for x in range(1,11):
...     print '%2d %3d %4d' % (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

(Merk op dat de spaties tussen iedere kolom te danken zijn aan de werking van het print-statement: het voegt altijd spaties toe tussen zijn argumenten.)

Dit voorbeeld toont de werking van de method rjust() van stringobjecten: de string wordt rechts uitgelijnd binnen een veld van een gegeven breedte, door deze aan de linkerkant op te vullen met spaties. Hieraan gerelateerd zijn de methods ljust() en center(), die een vergelijkbare werking hebben. Deze methods drukken zelf niets af, maar geven alleen een nieuwe string terug. Als de invoerstring te lang is, wordt deze niet afgekapt, maar ongewijzigd teruggegeven; hierdoor wordt weliswaar je kolomopmaak verstoord, maar in de meeste gevallen is dat beter dan het alternatief, nl. het tonen van een foutieve waarde. (Als je toch de voorkeur geeft aan het afkappen van strings, kun je altijd nog een slice-operatie toevoegen, zoals “x.ljust(n)[:n]”.)

De method zfill() vult een numerieke string aan de linkerkant op met nullen. Hij kan overweg met plus- en mintekens:

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

Het gebruik van de %-operator ziet er als volgt uit:

>>> import math
>>> print 'De waarde van PI is bij benadering %5.3f.' % math.pi
De waarde van PI is bij benadering 3.142.

Als je meer dan één opmaakinstructie hebt in je string, moet je een tuple meegeven als rechteroperand, zoals in dit voorbeeld:

>>> tabel = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for naam, telefoon in tabel.items():
...     print '%-10s ==> %10d' % (naam, telefoon)
... 
Jack       ==>       4098
Dcab       ==>       7678
Sjoerd     ==>       4127

De meeste opmaakinstructies (formats) werken exact hetzelfde als in C, en vereisen dat een argument van het juiste datatype wordt meegegeven; echter, doe je dat niet, dan krijg je geen core dump terug maar een exception. Het format %s is wat vergevingsgezinder: als het corresponderende argument geen stringobject is, wordt het geconverteerd naar een string met behulp van de ingebouwde functie str(). Het gebruik van * om de breedte of precisie als apart (integer)argument mee te geven, wordt ondersteund. De C-formats %n en %p worden niet ondersteund.

Als je een heel lange string met opmaakinstructies hebt, en je wilt deze niet opsplitsen, dan zou het handig zijn als je aan de te formatteren variabelen kon refereren op basis van hun naam, in plaats van hun positie. Dat is mogelijk met behulp van de vorm %(naam)format, zoals je hieronder ziet:

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

Dit is vooral handig in combinatie met de nieuwe ingebouwde functie vars(), die een dictionary met alle lokale variabelen teruggeeft.


7.2 Lezen uit en schrijven naar bestanden

open() geeft een file-object terug, en wordt meestal gebruikt met twee argumenten: "open(filenaam, modus)".

>>> f=open('/tmp/workfile', 'w')
>>> print f
<open file '/tmp/workfile', mode 'w' at 80a0960>

Het eerste argument is een string met de bestandsnaam. Het tweede argument is ook een string, met daarin een paar karakters die de wijze (modus) beschrijven waarop het bestand gebruikt zal worden. Met modus 'r' wordt een bestand geopend voor alleen-lezen, 'w' staat voor write-only (als er al een bestand met de gegeven naam aanwezig is, zal dat overschreven worden), en met'a' open je het bestand voor append: gegevens die naar het bestand worden geschreven, worden automatisch aan het eind toegevoegd. 'r+' opent het bestand voor zowel lees- als schrijfacties. modus is optioneel; wordt het weggelaten, dan is 'r' de default.

Onder Windows en Macintosh kun je nog 'b' toevoegen aan de modus; het bestand wordt dan in binaire modus (binary mode) geopend. Zo zijn dus ook de modi 'rb', 'wb', 'ab', en 'r+b'beschikbaar. Windows maakt onderscheid tussen tekstbestanden en binaire bestanden; in tekstbestanden worden bij het lezen of schrijven van gegevens de end-of-line karakters automatisch enigszins gewijzigd. Deze wijziging achter de schermen is geen probleem voor ASCII-tekstbestanden, maar binaire bestanden zoals JPEGs of .EXE's raken er corrupt van. Vergeet dus niet de binaire modus te gebruiken als je uit dit soort bestanden leest en/of ernaar wegschrijft. (Hou er rekening mee dat op de Macintosh de exacte semantiek van de tekstmodus bepaald wordt door de onderliggende C library die door Python gebruikt wordt.)


7.2.1 Methods van file-objecten

In alle voorbeelden hierna zullen we aannemen dat reeds een file-object met de naam f is aangemaakt.

Om de inhoud van een bestand te lezen, roep je f.read(size) aan; hiermee wordt een bepaalde hoeveelheid data ingelezen en teruggegeven als een string. size is een optioneel numeriek argument. Als size weggelaten wordt of negatief is, wordt de complete inhoud van het bestand gelezen en teruggegeven; mocht het bestand twee keer zo groot zijn als het geheugen van je machine, dan beschouwt Python dat als jouw probleem. Wordt wel een positieve waarde voor size meegegeven, dan worden hooguit size bytes gelezen en teruggegeven. Wordt f.read() aangeroepen nadat het einde van het bestand bereikt is, dan wordt een lege string ("") teruggegeven.

>>> f.read()
'Dit is het complete bestand.\n'
>>> f.read()
''

f.readline() leest een enkele regel uit het bestand; het newline-karakter(\n) aan het eind van de regel blijft behouden, dit wordt alleen weggelaten bij de laatste regel uit het bestand, als die niet met een newline eindigt. Hierdoor is ondubbelzinnig duidelijk wat de teruggegeven waarde precies voorstelt: als f.readline() een lege string teruggeeft, is het einde van het bestand bereikt; een lege regel wordt weergegeven met '\n', dus een string met alleen een enkele newline.

>>> f.readline()
'Dit is de eerste regel van het bestand.\n'
>>> f.readline()
'Tweede regel van het bestand\n'
>>> f.readline()
''

f.readlines() geeft een lijst terug met alle regels uit het bestand. Wordt de optionele parameter sizehint meegegeven, dan wordt het gegeven aantal bytes gelezen plus het aantal bytes wat nodig is om de laatst ingelezen regel te completeren; de ingelezen regels worden teruggegeven. Deze optie wordt vaak gebruikt om een groot bestand op efficiënte wijze regelgewijs in te lezen zonder dat het complete bestand in het geheugen gehaald moet worden. Er worden dus alleen complete regels teruggegeven.

>>> f.readlines()
['Dit is de eerste regel van het bestand.\n', 'Tweede regel van het bestand\n']

f.write(string) schrijft de inhoud van string naar het bestand, en geeft None terug.

>>> f.write('Dit is een test\n')

f.tell() geeft een integer terug die de huidige positie binnen het file-object weergeeft, gemeten in bytes vanaf het begin van het bestand. Om de positie binnen het file-object te wijzigen, gebruik je "f.seek(offset, from_what)". De nieuwe positie wordt berekend door offset op te tellen bij een referentiepunt; dit referentiepunt wordt geselecteerd met behulp van het argument from_what. De waarde 0 voor from_what verwijst naar het begin van het bestand, 1 staat voor de huidige positie in het bestand, en 2 staat voor het einde van het bestand als referentiepunt. from_what kan weggelaten worden; de default is 0, dus standaard wordt geteld vanaf het begin van het bestand.

>>> f=open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5)     # Ga naar de zesde byte in het bestand
>>> f.read(1)        
'5'
>>> f.seek(-3, 2) # Ga naar de derde byte vóór het einde
>>> f.read(1)
'd'

Als je klaar bent met een bestand, roep je f.close() aan om het te sluiten en alle systeemresources die door het bestand in beslag genomen werden, vrij te geven. Na aanroep van f.close(), zullen alle pogingen om het file-object te gebruiken, automatisch mislopen.

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

File-objecten hebben nog een aantal andere methods die minder vaak gebruikt worden, zoals isatty() en truncate(); voor een compleet overzicht van de mogelijkheden van file-objecten verwijzen we naar de Library Reference.


7.2.2 De module pickle

Het is eenvoudig om strings weg te schrijven naar en in te lezen uit een bestand. Voor getallen is wat meer inspanning nodig, aangezien de method read() alleen strings teruggeeft, die dan vervolgens geconverteerd moeten worden met een method als int(); hieraan wordt een string à la '123' meegegeven, die vervolgens teruggegeven wordt als zijn numerieke waarde: 123. Als je echter complexere datatypes zoals lists, dictionaries, of class instances wilt opslaan, worden de zaken nog veel ingewikkelder.

Om te voorkomen dat gebruikers iedere keer weer zelf de code moeten schrijven (en debuggen) voor het opslaan van complexe datatypes, biedt Python zelf een standaardmodule hiervoor aan, genaamd pickle. Dit is een zeer bijzondere module die van vrijwel ieder Python-object (en zelfs van sommige vormen van Python-code) een stringrepresentatie kan maken; dit process wordt pickling genoemd. Het opnieuw opbouwen van een object vanuit zijn stringrepresentatie heet unpickling. Tussen het moment van pickling en het moment van unpickling kan er van alles met de stringrepresentatie gebeurd zijn: hij kan opgeslagen zijn in een bestand, maar hij kan ook via een netwerkverbinding verzonden zijn naar een andere machine.

Stel dat je een object x hebt, en een file-object f dat geopend is voor schrijven, dan kan het object met één regel code gepickled worden:

pickle.dump(x, f)

Als je het file-object f voor lezen geopend hebt, kun je het object als volgt unpicklen:

x = pickle.load(f)

(Hier zijn diverse variaties op, die gebruikt worden om als je meerdere objecten tegelijk wilt picklen, of als je iets anders van plan met de gepicklede data dan wegschrijven naar een bestand. De complete documentatie voor pickle kun je vinden in de Python Library Reference.)

pickle is de standaardmanier voor het genereren van Python-objecten die kunnen worden opgeslagen en hergebruikt door andere programma's of in een toekomstige run van hetzelfde programma; de technische term hiervoor is een persistent object. Omdat pickle zo algemeen gebruikt wordt, besteden veel schrijvers van Python-extensies grote zorg aan het faciliteren van pickling en unpickling van nieuwe datatypes zoals matrices.


Previous Page

Up One Level

Next Page

Python Tutorial

Contents

Index

Vorige: 6. Modules Omhoog: Python Tutorial Volgende: 8. Fouten en exceptions
Release 2.3.4, documentation updated on May 20, 2004.
See About this document... for information on suggesting changes.