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
locale
modult, é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
with
utasí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 awith
kó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, „Awith
blokkban 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
newline
paramé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.StringIO
lehetővé teszi egy karakterlánc szövegfájlként kezelését. Létezik egyio.BytesIO
osztá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
gzip
modullal, 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árawith
utasí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
with
utasí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 egyetlenwith
utasí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