Itt vagy: Kezdőlap ‣ Ugorj fejest a Python 3-ba ‣
Nehézségi szint: ♦♦♦♢♢
❝ Egy kilenc mérföldes séta nem vicc, különösen nem az esőben. ❞
– Harry Kemelman, The Nine Mile Walk
A windowsos laptopomon 38 493 fájl volt, mielőtt egyetlen alkalmazást is telepítettem volna. A Python 3 telepítése majdnem 3 000 fájlt adott ehhez a számhoz. A fájlok minden jelentős operációs rendszer elsődleges tárolási paradigmáját jelentik, a fogalom annyira be van égve, hogy egyeseknek gondot okoz elképzelni az alternatívákat. A számítógéped képletesen szólva fuldoklik a fájlokban.
Mielőtt olvashatnál egy fájlból, meg kell azt nyitnod. A fájlok Pythonból való megnyitása nem is lehetne egyszerűbb:
egy_fájl = open('examples/chinese.txt', encoding='utf-8')
A Python rendelkezik egy beépített open() függvénnyel, amely egy fájlnevet vár argumentumként. A fájlnév itt az 'examples/chinese.txt'. A fájlnévvel kapcsolatban öt édekességet figyelhetünk meg:
open() függvény csak egyet vár. A Pythonban amikor egy „fájlnévre” van szükség, megadhatod egy könyvtárútvonal egy részét vagy egészét is.
Azonban az open() függvény hívása nem állt meg a fájlnévnél. Van még egy argumentum, amelynek neve encoding (kódolás). Jaj ne, ez félelmetesen ismerősen hangzik.
A bájtok bájtok, a karakterek absztrakciók. A karakterlánc Unicode karakterek sorozata. De egy lemezen lévő fájl nem Unicode karakterek sorozata, a lemezen lévő fájl bájtok sorozata. De hogyan konvertálja a Python a bájtsorozatot karakterek sorozatává, amikor beolvasol egy „szövegfájlt” a lemezről? A bájtokat egy adott karakterkódolási algoritmus szerint dekódolja, és visszaadja Unicode karakterek egy sorozatát (más néven egy karakterláncot).
# Ez a példa Windowson készült. Más operációs rendszerek
# másképp viselkedhetnek, a lentebb vázolt okokból.
>>> fájl = open('examples/chinese.txt')
>>> egy_karakterlánc = fájl.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 28: character maps to <undefined>
>>>
Mi történt? Nem adtál meg karakterkódolást, így a Pythonnak az alapértelmezett kódolást kell használnia. Mi az alapértelmezett kódolás? Ha alaposan megnézed a nyomkövetést, láthatod, hogy a cp1252.py fájlban keletkezik a kivétel, vagyis a Python alapértelmezett kódolása itt a CP-1252. (A CP-1252 egy gyakori kódolás a Microsoft Windowst futtató számítógépeken.) A CP-1252 karakterkészlet nem támogatja a fájlban lévő karaktereket, így az olvasás egy ronda UnicodeDecodeError kivétellel meghiúsul.
De várj, a helyzet ennél is rosszabb! Az alapértelmezett kódolás platformfüggő, így ez a kód akár működhet is a számítógépeden (ha az alapértelmezett kódolásod UTF-8), de hibát okoz, ha odaadod valaki másnak (akinek az alapértelmezett kódolása eltérő, mint például a CP-1252).
☞Ha meg szeretnéd kapni az alapértelmezett karakterkódolást, akkor importáld a
localemodult, és hívd meg alocale.getpreferredencoding()metódust. A windowsos laptopomon ez a'cp1252'értéket adja vissza, de az emeleten lévő linuxos gépemen az'UTF8'értéket. Még a saját házamban sem tudok rendet tartani! A te eredményeid eltérhetnek (még Windowson is) az operációs rendszer telepített verziójától és a területi/nyelvi beállításaidtól függően. Ezért annyira fontos megadni a kódolást minden alkalommal, amikor megnyitsz egy fájlt.
Egyelőre azt tudjuk, hogy a Python rendelkezik egy beépített, open() nevű függvénnyel. Az open() függvény egy adatfolyam objektumot ad vissza, amely rendelkezik a karakterek folyamának kezelésére és az azzal kapcsolatos információk lekérésére szolgáló metódusokkal és attribútumokkal.
>>> egy_fájl = open('examples/chinese.txt', encoding='utf-8')
>>> egy_fájl.name ①
'examples/chinese.txt'
>>> egy_fájl.encoding ②
'utf-8'
>>> egy_fájl.mode ③
'r'
name attribútum az open() függvénynek a fájl megnyitásakor átadott nevet tükrözi. Ez nincs abszolút útvonalnévvé normalizálva.
encoding attribútum az open() függvénynek a fájl megnyitásakor átadott kódolást tükrözi. Ha nem adtad meg a kódolást a fájl megnyitásakor (rossz fejlesztő!), akkor az encoding attribútum a locale.getpreferredencoding() értékét fogja tükrözni.
mode attribútum megadja, hogy milyen módban lett megnyitva a fájl. Az open() függvénynek átadhatsz egy elhagyható mode paramétert is. Nem adtál meg módot a fájl megnyitásakor, így a Python alapértelmezésben az 'r', azaz „megnyitás csak olvasásra, szöveges módban ” módot használja. Ahogyan majd később ebben a fejezetben látni fogod, a fájl módja több célt szolgál, a különböző módok lehetővé teszik a fájlba írást, fájlhoz fűzést, vagy fájl megnyitását bináris módban (amikor szövegek helyett bájtokat kezelsz). ☞Az
open()függvény dokumentációja felsorolja az összes lehetséges fájlmódot.
Miután megnyitottál egy fájlt olvasásra, előbb-utóbb olvasni akarsz majd belőle.
>>> egy_fájl = open('examples/chinese.txt', encoding='utf-8')
>>> egy_fájl.read() ①
'Dive Into Python 是为有经验的程序员编写的一本 Python 书。\n'
>>> egy_fájl.read() ②
''
read() metódusának hívásából áll. Az eredmény egy karakterlánc.
Mi van, ha újra akarod olvasni a fájlt?
# az előző példa folytatása >>> egy_fájl.read() ① '' >>> egy_fájl.seek(0) ② 0 >>> egy_fájl.read(16) ③ 'Dive Into Python' >>> egy_fájl.read(1) ④ ' ' >>> egy_fájl.read(1) '是' >>> egy_fájl.tell() ⑤ 20
read() metódusának további hívásai egyszerűen egy üres karakterláncot adnak vissza.
seek() metódus egy adott bájtpozícióra lép a fájlban.
read() metódus egy elhagyható paramétert vár, a beolvasandó karakterek számát.
Fussunk neki ennek még egyszer
# az előző példa folytatása >>> egy_fájl.seek(17) ① 17 >>> egy_fájl.read(1) ② '是' >>> egy_fájl.tell() ③ 20
Látod már? A seek() és tell() metódusok mindig bájtokat számolnak, de mivel a fájlt szövegként nyitottad meg, a read() metódus karaktereket számol. A kínai karakterek több bájtot igényelnek az UTF-8 kódoláshoz. A fájlban lévő angol karakterek egyenként csak egy bájtot igényelnek, így azt hiheted, hogy a seek() és a read() metódusok ugyanazt számolják. De ez csak néhány karakterre igaz.
De várj, lesz ez még rosszabb is!
>>> egy_fájl.seek(18) ① 18 >>> egy_fájl.read(1) ② Traceback (most recent call last): File "<pyshell#12>", line 1, in <module> egy_fájl.read(1) File "C:\Python31\lib\codecs.py", line 300, in decode (result, consumed) = self._buffer_decode(data, self.errors, final) UnicodeDecodeError: 'utf8' codec can't decode byte 0x98 in position 0: unexpected code byte
UnicodeDecodeError kivételt dob.
A nyitott fájlok erőforrásokat fogyasztanak, és a fájl módjától függően más programok esetleg nem tudják elérni azokat. Fontos a fájlokat azonnal bezárni, amint végeztél a feldolgozásukkal.
# folytatás az előző példából >>> egy_fájl.close()
Hát ez nem volt túl izgalmas.
Az egy_fájl nevű adatfolyam-objektum továbbra is létezik, a close() metódusának meghívása nem semmisíti meg magát az objektumot. De azért így nem túl hasznos.
# az előző példa folytatása >>> egy_fájl.read() ① Traceback (most recent call last): File "<pyshell#24>", line 1, in <module> a_file.read() ValueError: I/O operation on closed file. >>> egy_fájl.seek(0) ② Traceback (most recent call last): File "<pyshell#25>", line 1, in <module> a_file.seek(0) ValueError: I/O operation on closed file. >>> egy_fájl.tell() ③ Traceback (most recent call last): File "<pyshell#26>", line 1, in <module> a_file.tell() ValueError: I/O operation on closed file. >>> egy_fájl.close() ④ >>> egy_fájl.closed ⑤ True
IOError kivételt okoz.
tell() metódus hívása is meghiúsul.
close() metódus hívása egy olyan adatfolyam-objektumon, amely fájlja be lett zárva, nem okoz kivételt. Egyszerűen nem csinál semmit.
closed attribútum megerősíti, hogy a fájl be van zárva.
Az adatfolyam-objektumok rendelkeznek explicit close() metódussal, de mi történik, ha a kódod hibás, és még a close() hívása előtt összeomlik? Az a fájl elméletileg sokkal tovább is nyitva maradhat, mint szükséges. Amíg a helyi számítógépen keresel hibát, ez nem nagy ügy. Egy éles szerveren már talán.
A Python 2-nek volt erre egy megoldása: a try..finally blokk. Ez továbbra is működik Python 3-ban, és láthatod is mások kódjában, vagy olyan kódban, amit Python 3-ra portoltak. De a Python 2.6 bevezetett egy tisztább megoldást, amit a Python 3-ban előnyben részesítenek: a with utasítást.
with open('examples/chinese.txt', encoding='utf-8') as egy_fájl:
egy_fájl.seek(17)
egy_karakter = egy_fájl.read(1)
print(egy_karakter)
Ez a kód meghívja az open() metódust, de soha nem jívja meg az egy_fájl.close()-t. A with utasítás kódblokkot kezd, mint egy if utasítás, vagy for ciklus. Ezen a kódblokkon belül az egy_fájl változót úgy használhatod, mint az open() által visszaadott adatfolyam-objektumot. Az összes normális adatfolyam-objektum metódus rendelkezésre áll – seek(), read(), amit csak akarsz. Amikor a with blokknak vége, a Python automatikusan meghívja az egy_fájl.close()-t.
Itt a trükk: nem számít, hogyan vagy mikor lépsz ki a with blokkból, a Python bezárja a fájlt… még ha egy nem kezelt kivételen át is „lépsz ki” belőle. Így van: még ha a kódod kivételt is dob, és az egész program csikorogva leáll, a fájl be lesz zárva. Garantáltan.
☞Technikailag a
withutasítás egy futási kontextust hoz létre. Ezekben a példákban az adatfolyam-objektum kontextuskezelőként működik. A Python létrehozza az egy_fájl nevű adatfolyam-objektumot, és közli vele, hogy most belép egy futási kontextusba. Amikor awithkódblokk befejeződött, a Python közli az adatfolyam-objektummal, hogy most kilép a futási kontextusból, és az adatfolyam-objektum meghívja a sajátclose()metódusát. Lásd a B függelék, „Awithblokkban használható osztályok” szakaszt a részletekért.
Nincs semmi fájlspecifikus a with utasításban; ez egy általános keretrendszer futási kontextusok létrehozására, és annak közlésére objektumokkal, hogy épp belépnek egy futási kontextusba vagy kilépnek abból. Ha a kérdéses objektum egy adatfolyam-objektum, akkor ez hasznos fájlközeli dolgokat csinál (például automatikusan bezárja a fájlt). De ez a viselkedés az adatfolyam-objektumban van meghatározva, nem a with utasításban. A kontextuskezelők használatára rengeteg más mód is van, a fájloktól függetlenül. Akár sajátot is létrehozhatsz, ahogyan majd a fejezet későbbi részében látni fogod.
A szövegfájlok egy „sora” pontosan az, aminek elképzeled – beírsz pár szót, és megnyomod az ENTER-t, és máris egy új sorban vagy. Egy szövegsor karakterek sorozata, amiket elválaszt egy… mi is pontosan? Nos, ez bonyolult, mert a szövegfájlok több különböző karaktert is használhatnak a sor végének jelzésére. Minden operációs rendszernek megvan a saját megállapodása. Néhány a kocsivissza karaktert használja, mások a soremelés karaktert, és néhány mindkettőt elhelyezi minden sor végén.
Lélegezz fel, mert a Python alapértelmezésben automatikusan kezeli a sorvégeket. Ha azt mondod, „Soronként be akarom olvasni ezt a szövegfájlt,I want” akkor a Python kitalálja, milyen sorvégződést használ a fájl, és innentől minden Csak Működik.
☞Ha részletesebben szeretnéd befolyásolni, hogy mi számít sorvégnek, akkor átadhatod a
newlineparamétert azopen()függvénynek. A véres részletekért lásd azopen()függvény dokumentációját.
Nos, akkor valójában hogy csinálod? Mármint a fájl soronkénti beolvasását. Olyan egyszerű, hogy gyönyörű.
line_number = 0
with open('examples/favorite-people.txt', encoding='utf-8') as egy_fájl: ①
for egy_sor in egy_fájl: ②
sorszám += 1
print('{:>4} {}'.format(sorszám, egy_sor.rstrip())) ③
with minta használatával biztonságosan megnyitod a fájlt, és hagyod, hogy Python bezárja helyetted.
for ciklust. Ennyi. Azon kívül, hogy a read()-hez hasonló explicit metódusai vannak, az adatfolyam-objektum egyben iterátor is, ami minden értékkéréskor egy sort ad vissza.
format() karakterlánc-metódus használatával kiírathatod a sorszámot és magát a sort is. A {:>4} formátumleíró azt jelenti: „írd ki ezt az argumentumot jobbra igazítva 4 szóközön belül.” Az egy_sor változó tartalmazza az egész sort, a kocsivissza karakterekkel együtt. Az rstrip() karakterlánc-metódus eltávolítja a záró üres helyeket, beleértve a kocsivissza karaktereket is.
you@localhost:~/diveintopython3$ python3 examples/oneline.py 1 Dora 2 Ethan 3 Wesley 4 John 5 Anne 6 Mike 7 Chris 8 Sarah 9 Alex 10 Lizzie
Ezt a hibát kaptad?
te@localhost:~/diveintopython3$ python3 examples/oneline.py Traceback (most recent call last): File "examples/oneline.py", line 4, in <module> print('{:>4} {}'.format(line_number, a_line.rstrip())) ValueError: zero length field name in formatHa igen, akkor valószínűleg a Python 3.0-át használod. Komolyan frissítened kellene legalább Python 3.1-re.
A Python 3.0 támogatta a karakterlánc-formázást, de csak expliciten számozottformátumleírókkal. A Python 3.1 lehetővé teszi az argumentumindexek kihagyását a formátumleírókban. Összehasonlításul itt a Python 3.0-kompatibilis verzió:
print('{0:>4} {1}'.format(sorszám, egy_sor.rstrip()))
⁂
A fájlokba nagyjából ugyanúgy írhatsz, ahogy olvasod azokat. Először megnyitsz egy fájlt, és kapsz egy adatfolyam-objektumot, aztán metódusokat használsz az adatfolyam-objektumon, hogy adatokat írj a fájlba, majd bezárod a fájlt.
Egy fájl írásra való megnyitásához használd az open() függvényt, és add meg az írás módot. Két fájlmód használható az íráshoz:
mode='w' paramétert az open() függvénynek.
mode='a' paramétert az open() függvénynek.
Mindkét mód automatikusan létrehozza a fájlt, ha még nem létezik, így nincs szükség semmi szöszmötölős „ha a fájl még nem létezik, akkor hozz létre egy új fájlt csak hogy először megnyithasd” függvényre. Csak nyisd meg a fájlt, és kezdd írni.
Mindig zárd be a fájlokat, amint végeztél az írásukkal, hogy felszabadítsd a fájlhivatkozást, és biztosítsd, hogy az adatok valójában kiírásra kerülnek a háttértárra. Ahogyan az adatok fájlból olvasásakor, itt is meghívhatod az adatfolyam-objektum close() metódusát, vagy használhatod a with utasítást, és rábízhatod a fájl bezárását a Pythonra. Fogadni mernék, hogy kitalálod, melyik módszert javaslom.
>>> with open('teszt.log', mode='w', encoding='utf-8') as egy_fájl: ①
... egy_fájl.write('a teszt sikerült') ②
>>> with open('teszt.log', encoding='utf-8') as egy_fájl:
... print(egy_fájl.read())
a teszt sikerült
>>> with open('teszt.log', mode='a', encoding='utf-8') as egy_fájl: ③
... a_file.write('és újra')
>>> with open('teszt.log', encoding='utf-8') as egy_fájl:
... print(egy_fájl.read())
a teszt sikerültés újra ④
teszt.log nevű új fájl létrehozásával (vagy a meglévő fájl felülírásával), és a fájl írásra megnyitásával. A mode='w' paraméter azt jelenti, hogy írásra nyitod meg a fájlt. Igen, ez tényleg olyan veszélyes, mint amilyennek hangzik. Remélem, nem érdekelt annak a fájlnak a tartalma (ha volt), mert azok az adatok már elvesztek.
open() függvény által visszaadott adatfolyam-objektum write() metódusával adhatsz adatokat az újonnan megnyitott fájlhoz. Awith blokk végén a Python automatikusan bezárja a fájlt.
mode='a' használatával, hogy hozzáfűzzünk a fájlhoz a felülírása helyett. A hozzáfűzéssoha nem bántja a fájl meglévő tartalmát.
teszt.log fájlban van. Vedd észre, hogy sem kocsivissza, sem soremelés karakterek nincsenek. Mivel nem írtál ki explicit módon ilyeneket egyik alkalommal sem, a fájl nem tartalmaz egyet sem. Kocsivissza karaktert a '\r' sorozat használatával, soremelést pedig a '\n' sorozattal írhatsz. Mivel egyiket sem tetted, a fájlba írt minden szöveg egy sorba került.
Észrevetted az encoding paramétert, amit az open() függvény kapott, miközben megnyitottál egy fájlt írásra? Ez fontos, soha ne hagyd ki! Ahogyan a fejezet elején láttad, a fájlok nem karakterláncokat tartalmaznak, hanembájtokat. A „karakterlánc” szövegfájlból olvasása csak azért működik, mert megmondtad a Pythonnak, hogy milyen kódolást használjon a bájtok sorozatának olvasásához, és karakterlánccá konvertálásához. A szöveg fájlba írása ugyanazt a problémát veti fel, csak fordítva. Nem írhatsz karaktereket egy fájlba,a karakterek absztrakciók. Ahhoz, hogy fájlba írhasd őket, a Pythonnak tudnia kell, hogyan konvertálja a karakterláncodat bájtok sorozatává. Egyetlen módszer van a helyes konverzió végrehajtására: az encoding paraméter megadása a fájl írásra való megnyitásakor.
⁂
Nem minden fájl tartalmaz szöveget. Néhány a kutyám képét tartalmazza.
>>> egy_kép = open('examples/beauregard.jpg', mode='rb') ①
>>> egy_kép.mode ②
'rb'
>>> egy_kép.name ③
'examples/beauregard.jpg'
>>> egy_kép.encoding ④
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: '_io.BufferedReader' object has no attribute 'encoding'
mode paraméter tartalmaz egy 'b' karaktert.
mode-ot, ami az open() függvénynek átadott mode paramétert tükrözi.
name attribútumuk is, ahogyan a szöveges adatfolyam-objektumoknak.
encoding attribútuma. Logikus, nem? Bájtokat, és nem karakterláncokat olvasol (vagy írsz), így nem kell a Pythonnak konverziót végeznie. Amit egy bináris fájlba teszel, pontosan azt veszed ki, nincs szükség konverzióra.
Említettem, hogy bájtokat olvasol? Ó, igen, azokat.
# folytatás az előző példából >>> egy_kép.tell() 0 >>> adat = egy_kép.read(3) ① >>> adat b'\xff\xd8\xff' >>> type(adat) ② <class 'bytes'> >>> egy_kép.tell() ③ 3 >>> egy_kép.seek(0) 0 >>> adat = egy_kép.read() >>> len(adat) 3150
read() metódus abeolvasandó bájtok számát várja, nem a karakterek számát.
read() metódusnak átadott szám és a tell() metódus által visszaadott pozícióindex között. A read() metódus bájtokat olvas, és a seek() és tell() metódusok a beolvasott bájtok számát tartják nyilván. A bináris fájlok esetén ezek mindig megegyeznek.
⁂
Képzeld el, hogy egy függvénytárat írsz, és a függvénytárad egyik függvénye adatokat fog olvasni egy fájlból. A függvény egyszerűen várhat egy fájlnevet karakterláncként, megnyithatja a fájlt olvasásra, és bezárhatja kilépés előtt. De ne csináld ezt. Ehelyett az API-d várhat egy tetszőleges adatfolyam-objektumot.
A legegyszerűbb esetben az adatfolyam-objektum bármi lehet, ha rendelkezik egy read() metódussal, ami egy elhagyható size paramétert vár, és visszaad egy karakterláncot. Amikor size paraméter nélkül hívják, a read() metódus be kell olvasson mindent a bemeneti forrásból, és vissza kell adnia az összes adatot egyetlen értékként. Amikor size paraméterrel hívják, annyit olvas a bemeneti forrásból, és annyi adatot ad vissza. Ha újra meghívják, ott folytatja, ahol abbahagyta, és visszaadja a következő adatdarabot.
Ez pontosan úgy hangzik, mint a valódi fájl megnyitásakor kapott adatfolyam-objektum. A különbség, hogy nem korlátozod magad valódi fájlokra. Az „olvasott” bemeneti forrás bármi lehet: egy weboldal, egy karakterlánc a memóriában, akár egy másik program kimenete is. Amíg a függvényeid egy adatfolyam-objektumot várnak, és egyszerűen meghívják az objektum read() metódusát, tetszőleges bemeneti forrást kezelhetsz, ami fájlként viselkedik, a különböző bemenettípusokat külön-külön kezelő kód nélkül.
>>> egy_karakterlánc = 'PapayaWhip is the new black.' >>> import io ① >>> egy_fájl = io.StringIO(egy_karakterlánc) ② >>> egy_fájl.read() ③ 'PapayaWhip is the new black.' >>> egy_fájl.read() ④ '' >>> egy_fájl.seek(0) ⑤ 0 >>> egy_fájl.read(10) ⑥ 'PapayaWhip' >>> egy_fájl.tell() 10 >>> egy_fájl.seek(18) 18 >>> egy_fájl.read() 'new black.'
io modul definiálja a StringIO osztályt, amelyet egy memóriabeli karakterlánc fájlként kezelésére használhatsz.
io.StringIO() osztályt, és add át neki a „fájladatként” használandó karakterláncot. Most már van egy adatfolyam-objektumod, és mindenféle adatfolyam-szerű dolgot csinálhatsz vele.
read() metódus hívása „beolvassa” az egész „fájlt,” ami a StringIO objektum esetén egyszerűen visszaadja az eredeti karakterláncot.
read() metódus ismételt hívása üres karakterláncot ad vissza.
StringIO objektum seek() metódusnak használatával.
read() metódusnak.
Az ☞
io.StringIOlehetővé teszi egy karakterlánc szövegfájlként kezelését. Létezik egyio.BytesIOosztály is, ami lehetővé teszi egy bájttömb bináris fájlként kezelését.
A Python szabványos függvénytára tartalmaz a tömörített fájlok olvasását és írását támogató modulokat. Számos különböző tömörítési eljárás létezik, a két legnépszerűbb nem Windows rendszereken a gzip és bzip2. (Találkozhattál PKZIP archívumokkal és GNU Tar archívumokkal is. A Python rendelkezik modulokkal ezekhez is.)
A gzip modul lehetővé teszi gzip-pel tömörített fájl olvasásához vagy írásához használható adatfolyam-objektum létrehozását. Az általa visszaadott adatfolyam-objektum támogatja a read() metódust (ha olvasásra nyitottad meg), vagy a write() metódust (ha írásra nyitottad meg). Ez azt jelenti, hogy használhatod a normális fájlokhoz megismert metódusokat gzip-pel tömörített fájl közvetlen olvasására vagy írására, anélkül, hogy ideiglenes fájlt kellene létrehoznod a kibontott adatok tárolásához.
További extraként támogatja a with utasítást is, így hagyhatod, hogy a Python automatikusan bezárja a gzip-pel tömörített fájlodat, ha végeztél vele.
te@localhost:~$ python3
>>> import gzip
>>> with gzip.open('out.log.gz', mode='wb') as z_fájl: ①
... z_fájl.write('Egy kilenc mérföldes séta nem vicc, különösen nem az esőben.'.encode('utf-8'))
...
>>> exit()
te@localhost:~$ ls -l out.log.gz ②
-rw-r--r-- 1 mark mark 79 2009-07-19 14:29 out.log.gz
te@localhost:~$ gunzip out.log.gz ③
te@localhost:~$ cat out.log ④
Egy kilenc mérföldes séta nem vicc, különösen nem az esőben.
'b' karaktert a mode argumentumban.)
gunzip parancs (ejtsd: „gé-unzip”) kibontja a fájlt, és a tartalmát egy új fájlban tárolja, aminek ugyanaz lesz a neve, mint a tömörített fájlnak, de a .gz fájlkiterjesztés nélkül.
cat parancs megjeleníti egy fájl tartalmát. Ez a fájl tartalmazza azt a karakterláncot, amit eredetileg közvetlenül az out.log.gz fájlba írtál a Python parancsértelmezőben.
Ezt a hibát kaptad?
>>> with gzip.open('out.log.gz', mode='wb') as z_fájl: ... z_fájl.write('Egy kilenc mérföldes séta nem vicc, különösen nem az esőben.'.encode('utf-8')) ... Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'GzipFile' object has no attribute '__exit__'Ha igen, akkor valószínűleg a Python 3.0-át használod. Komolyan frissítened kellene legalább Python 3.1-re.
A Python 3.0 rendelkezett egy
gzipmodullal, de nem támogatta a gzip-pel tömörített fájl objektum kontextuskezelőként való használatát. A Python 3.1 már képes a gzip-pel tömörített fájl objektumok használatárawithutasításban.
⁂
A parancssori guruknak már ismerős a szabványos bemenet, szabványos kimenet és szabványos hibakimenet fogalma. Ez a szakasz a többieknek szól.
A szabványos kimenet és a szabványos hibakimenet (gyakran stdout és stderr rövidítésekkel használják) olyan adatcsatornák, amik minden UNIX-szerű rendszerbe be vannak építve, beleértve a Mac OS X-et és Linuxot. Amikor a print() függvényt hívod, a kiírt dolog az stdout adatcsatornába kerül. Amikor a programod összeomlik, és nyomkövetést ír ki, az az stderr adatcsatornába kerül. Alapértelmezésben ez a két adatcsatorna egyaránt arra a terminálablakra van irányítva, amiben dolgozol, amikor a programod kiír valamit, a kimenetet a terminálablakban látod, és amikor a program összeomlik, az is a terminálablakban jeleníti meg a nyomkövetést. A grafikus Python parancsértelmezőben az stdout és stderr adatcsatornák alapértelmezésben az „interaktív ablakba” vannak irányítva.
>>> for i in range(3):
... print('PapayaWhip') ①
PapayaWhip
PapayaWhip
PapayaWhip
>>> import sys
>>> for i in range(3):
... l = sys.stdout.write('is the') ②
is theis theis the
>>> for i in range(3):
... l = sys.stderr.write('new black') ③
new blacknew blacknew black
print() függvény, ciklusban. Semmi meglepő.
stdout a sys modulban van definiálva, és ez egy adatfolyam-objektum. A write() függvényének meghívása kiírja a neki átadott tetszőleges karakterláncot, majd visszaadja a kimenet hosszát. Valójában ez az, amit a print függvény ténylegesen csinál; hozzáad egy kocsivissza karaktert a kiírandó karakterlánc végéhez, és meghívja a sys.stdout.write-ot.
sys.stdout és a sys.stderr a kimenetüket ugyanoda küldik: a Python IDE-nek (ha ezt használod) vagy a terminálnak (ha a Pythont a parancssorból futtatod). A szabványos kimenethez hasonlóan a szabványos hibakimenet sem ad kocsivissza karaktereket a szöveghez. Ha kocsivissza karaktereket akarsz, akkor kocsivissza karaktereket kell írnod.
A sys.stdout és sys.stderr adatfolyam-objektumok, de csak írhatók. A read() metódusuk hívására tett kísérlet mindig IOError kivételt dob.
>>> import sys >>> sys.stdout.read() Traceback (most recent call last): File "<stdin>", line 1, in <module> IOError: not readable
A sys.stdout és sys.stderr adatfolyam-objektumok, noha csak az írást támogatják. De nem konstansok, hanem változók. Ez azt jelenti, hogy új értéket rendelhetsz hozzájuk – bármely másik adatfolyam-objektumot – hogy az irányítsa át a kimenetét.
import sys
class RedirectStdoutTo:
def __init__(self, ki_új):
self.ki_új = ki_új
def __enter__(self):
self.ki_régi = sys.stdout
sys.stdout = self.ki_új
def __exit__(self, *args):
sys.stdout = self.ki_új
print('A')
with open('out.log', mode='w', encoding='utf-8') as egy_fájl, RedirectStdoutTo(egy_fájl):
print('B')
print('C')
Nézd csak:
te@localhost:~/diveintopython3/examples$ python3 stdout.py A C te@localhost:~/diveintopython3/examples$ cat out.log B
Ezt a hibát kaptad?
te@localhost:~/diveintopython3/examples$ python3 stdout.py File "stdout.py", line 15 with open('out.log', mode='w', encoding='utf-8') as egy_fájl, RedirectStdoutTo(egy_fájl): ^ SyntaxError: invalid syntaxHa igen, akkor valószínűleg a Python 3.0-át használod. Komolyan frissítened kellene legalább Python 3.1-re.
A Python 3.0 támogatta a
withutasítást, de minden utasítás csak egy kontextuskezelőt tud használni. A Python 3.1 lehetővé teszi több kontextuskezelő összekötését egyetlenwithutasításban.
Vegyük először az utolsó részt.
print('A')
with open('out.log', mode='w', encoding='utf-8') as egy_fájl, RedirectStdoutTo(egy_fájl):
print('B')
print('C')
Ez egy bonyolult with utasítás. Hadd írjam át valami felismerhetőbbre.
with open('out.log', mode='w', encoding='utf-8') as egy_fájl:
with RedirectStdoutTo(egy_fájl):
print('B')
Ahogyan az újraírás mutatja, valójában két with utasításunk van, az egyik beágyazva a másik hatókörébe. A „külső” with utasításnak mostanra ismerősnek kell lennie: megnyit egy UTF-8-kódolású, out.log nevű fájlt írásra, és az adatfolyam-objektumot hozzárendeli az egy_fájl nevű változóhoz. De itt nem ez az egyetlen fura dolog.
with RedirectStdoutTo(egy_fájl):
Hol van az as kulcsszó? Valójában a with utasítás nem igényli. Mint ahogy meghívsz egy függvényt, és figyelmen kívül hagyod a visszatérési értékét, ugyanúgy használhatsz olyan with utasítást, ami nem rendeli a with kontextust változóhoz. Ebben az esetben csak a RedirectStdoutTo kontextus mellékhatásai érdekelnek.
Mik ezek a mellékhatások? Vessünk egy pillantást a RedirectStdoutTo osztályra. Ez az osztály egy egyéni kontextuskezelő. Bármely osztály lehet kontextuskezelő, ezen két speciális metódus definiálásával: __enter__() és __exit__().
class RedirectStdoutTo:
def __init__(self, ki_új): ①
self.ki_új = ki_új
def __enter__(self): ②
self.ki_régi = sys.stdout
sys.stdout = self.ki_új
def __exit__(self, *args): ③
sys.stdout = self.ki_régi
__init__() metódus a példány létrehozása után azonnal meghívásra kerül. Egy paramétert vár, a kontextus élete során szabványos kimenetként használni kívánt adatfolyam-objektumot. Ez a metódus egyszerűen egy példányváltozóban menti az adatfolyam-objektumot, hogy később más metódusok is használhassák.
__enter__() metódus egy speciális osztálymetódus. A Python akkor hívja meg, amikor belép a kontextusba (azaz a with utasítás elején). Ez a metódus elmenti a sys.stdout aktuális értékét a self.ki_régi változóba, majd átirányítja a szabványos kimenetet a self.ki_új hozzárendelésével a sys.stdout-hoz.
__exit__() metódus egy másik speciális osztálymetódus. A Python a kontextusból való kilépéskor hívja meg (azaz a with utasítás végén). Ez a metódus visszaállítja a szabványos kimenetet az eredeti értékére az elmentett self.ki_régi hozzárendelésével a sys.stdout-hoz.
Összegezve:
print('A') ①
with open('out.log', mode='w', encoding='utf-8') as egy_fájl, RedirectStdoutTo(a_file): ②
print('B') ③
print('C') ④
with utasítás kontextusok vesszőkkel elválasztott listáját várja. A vesszőkkel elválasztott lista egymásba ágyazott with blokkok sorozataként működik. Az elsőként felsorolt kontextus a „külső” blokk, az utolsóként felsorolt a „belső” blokk. Az első kontextus megnyit egy fájlt, a második kontextus átirányítja a sys.stdout-ot az első kontextusban létrehozott adatfolyam-objektumra.
print() függvény a with utasítással létrehozott kontextussal kerül végrehajtásra, nem a képernyőre fog írni, hanem az out.log fájlba.
with kódblokknak vége van. A Python közölte minden egyes kontextuskezelővel, hogy tegyék meg azt, amit a kontextusból kilépéskor szoktak. A kontextuskezelők egy utolsó-be, első-ki (LIFO) vermet alkotnak. Kilépéskor a második kontextus visszaállította a sys.stdout-ot az eredeti értékére, majd az első kontextus bezárta az out.log nevű fájlt. Mivel a szabványos kimenet vissza lett állítva az eredeti értékére, a print() függvény hívása újra a képernyőre fog írni.
A szabványos hibakimenet átirányítása pontosan ugyanígy működik, a sys.stderr-t használva a sys.stdout helyett.
⁂
io modul
sys.stdout és sys.stderr
© 2001–11 Mark Pilgrim