Itt vagy: Kezdőlap ‣ Ugorj fejest a Python 3-ba ‣
Nehézségi szint: ♦♦♢♢♢
❝Képzeletünk a végsőkig van feszítve, nem azért, hogy irodalmi, nem létező dolgokat képzeljünk el, hanem hogy megértsük a valóban létezőket. ❞
– Richard Feynman
Minden programozási nyelvnek van egy szolgáltatása, egy bonyolult dolog, amelyet szándékosan egyszerűvé tesz. Ha más nyelvet használsz, könnyen lemaradhatsz róla, mert a régi nyelv nem tette azt a dolgot egyszerűvé (mert inkább valami mást tett egyszerűvé). Ez a fejezet elmagyarázza a listafeldolgozókat, szótárfeldolgozókat és halmazfeldolgozókat: három kapcsolódó fogalom, amelyek egy nagyon hatékony módszer köré csoportosulnak. De előbb tegyünk egy kis kitérőt két modulra, amelyek segíteni fognak a helyi fájlrendszeren való navigációban.
⁂
A Python 3 tartalmaz egy os
nevű modult, amely az „operating system” (operációs rendszer) rövidítése. Az os
modul rengeteg függvényt tartalmaz, amelyekkel információk kérhetők a helyi könyvtárakról, fájlokról, folyamatokról és környezeti változókról – és egyes esetekben ezek kezelhetők is. A Python mindent megtesz azért, hogy egy egységesített API-t kínáljon minden támogatott operációs rendszeren, így programjaid bármely számítógépen a lehető legkevesebb platformspecifikus kóddal futhatnak.
Ha kezdő vagy a Pythonban, akkor rengeteg időt fogsz eltölteni a Python Shellben. Ebben a könyvben a következőhöz hasonló példákat fogsz látni:
examples
mappából
Ha nem ismered az aktuális munkakönyvtárat, akkor az első lépés valószínűleg meghiúsul egy ImportError
kivétellel. Miért? Mert a Python a példa modult az importálás keresési útvonalán fogja keresni, de nem fogja megtalálni, mert az examples
mappa nem a keresési útvonal egyik könyvtára. Ennek megoldásához a következő két lehetőség egyikét használhatod:
examples
mappát az importálás keresési útvonalához
examples
mappára
Az aktuális munkakönyvtár egy láthatatlan tulajdonság, amelyet a Python mindig a memóriában tárol. Mindig van aktuális munkakönyvtár, akár a Python Shellben vagy, akár egy Python parancsfájlt futtatsz a parancssorból, akár egy Python CGI parancsfájlt futtatsz egy webkiszolgálón.
Az os
modul két függvényt tartalmaz az aktuális munkakönyvtár kezeléséhez.
>>> import os ① >>> print(os.getcwd()) ② C:\Python32 >>> os.chdir('/Users/pilgrim/diveintopython3/examples') ③ >>> print(os.getcwd()) ④ C:\Users\pilgrim\diveintopython3\examples
os
modul a Python része: bármikor, bárhol importálhatod.
os.getcwd()
függvény használatával lekérdezheted az aktuális munkakönyvtárat. Ha a grafikus Python Shellt futtatod, akkor az aktuális munkakönyvtár az, ahonnan a Python Shell végrehajtható. Windowson ez attól függ, hogy hová telepítetted a Pythont; az alapértelmezett könyvtár a c:\Python32
. Ha a Python Shellt a parancssorból futtatod, akkor az aktuális munkakönyvtár az, ahol a python3
elindításakor álltál.
os.chdir()
függvény használatával megváltoztathatod az aktuális munkakönyvtárat. os.chdir()
függvényt meghívtam, akkor egy Linux-stílusú útvonalnevet használtam (osztásjel, meghajtó betűjel nélkül) noha épp Windowst használtam. Ez az egyik olyan hely, ahol a Python megpróbálja eltüntetni az operációs rendszerek közti különbségeket.
Ha már a könyvtáraknál vagyunk, szeretném bemutatni az os.path
modult. Az os.path
fájlnevek és könyvtárnevek kezelésére szolgáló függvényneveket tartalmaz.
>>> import os >>> print(os.path.join('/Users/pilgrim/diveintopython3/examples/', 'humansize.py')) ① /Users/pilgrim/diveintopython3/examples/humansize.py >>> print(os.path.join('/Users/pilgrim/diveintopython3/examples', 'humansize.py')) ② /Users/pilgrim/diveintopython3/examples\humansize.py >>> print(os.path.expanduser('~')) ③ c:\Users\pilgrim >>> print(os.path.join(os.path.expanduser('~'), 'diveintopython3', 'examples', 'humansize.py')) ④ c:\Users\pilgrim\diveintopython3\examples\humansize.py
os.path.join()
függvény legalább egy részleges útvonalnévből készít egy útvonalnevet. Ebben az esetben egyszerűen összefűzi a karakterláncokat.
os.path.join()
függvény egy extra osztásjelet ad az útvonalnévhez a fájlnévhez fűzés előtt. Ez egy fordított törtvonal az osztásjel helyett, mert a példa Windowson készült. Ha ezt a példát Linuxon vagy Mac OS X-en hajtod végre, akkor ehelyett egy osztásjelet kapsz. Ne vacakolj az osztásjelekkel, mindig használd az os.path.join()
függvényt, és a Python megoldja.
os.path.expanduser()
függvény kiterjeszti az aktuális felhasználó saját könyvtárának ábrázolására ~
jelet használó útvonalnevet. Ez minden platformon működik, ahol a felhasználók rendelkeznek saját könyvtárral, beleértve a Linux, Mac OS X és Windows rendszereket. A visszaadott útvonal nem rendelkezik záró osztásjellel, de az os.path.join()
függvényt ez nem zavarja.
os.path.join()
függvény tetszőleges számú argumentumot képes feldolgozni. Rettenetesen megörültem, amikor ezt felfedeztem, mivel az egyik kis hülye függvényem az addSlashIfNecessary()
, amelyet mindig meg kell írnom a szerszámos ládám egy új nyelven való kialakításakor. Ne írd meg ezt a kis hülye függvényt Pythonban, ezt a problémát okos emberek már megoldották helyetted.
Az os.path
függvényeket tartalmaz a teljes útvonalnevek, könyvtárnevek és fájlnevek felosztására az azokat alkotó részekre.
>>> útvonal = '/Users/pilgrim/diveintopython3/examples/humansize.py' >>> os.path.split(útvonal) ① ('/Users/pilgrim/diveintopython3/examples', 'humansize.py') >>> (kvtnév, fájlnév) = os.path.split(útvonal) ② >>> kvtnév ③ '/Users/pilgrim/diveintopython3/examples' >>> fájlnév ④ 'humansize.py' >>> (rövidnév, kiterjesztés) = os.path.splitext(fájlnév) ⑤ >>> rövidnév 'humansize' >>> kiterjesztés '.py'
split
függvény feloszt egy teljes útvonalnevet, és visszaad egy, az útvonalat és a fájlnevet tartalmazó tuple-t.
os.path.split()
függvény pontosan ezt csinálja. A split
függvény visszatérési értékét egy két változót tartalmazó tuple-höz rendeli. Minden változó a visszaadott tuple megfelelő elemének értékét kapja.
os.path.split()
által visszaadott tuple első elemének értékét, a fájl útvonalát.
os.path.split()
által visszaadott tuple második elemének értékét, a fájl nevét.
os.path
tartalmazza az os.path.splitext()
függvényt is, amely feloszt egy fájlnevet, és visszaadja a fájlnevet és a fájlkiterjesztést tartalmazó tuple-t. Ugyanezzel a módszerrel ezeket is önálló változókhoz rendelheted.
A glob
modul a szabványos Python függvénytár másik eszköze. Egyszerű módszert nyújt egy könyvtár tartalmának lekérésére a programodból, és a parancssor használata során már remélhetőleg megszokott helyettesítő karaktereket használja.
>>> os.chdir('/Users/pilgrim/diveintopython3/') >>> import glob >>> glob.glob('examples/*.xml') ① ['examples\\feed-broken.xml', 'examples\\feed-ns0.xml', 'examples\\feed.xml'] >>> os.chdir('examples/') ② >>> glob.glob('*test*.py') ③ ['alphameticstest.py', 'pluraltest1.py', 'pluraltest2.py', 'pluraltest3.py', 'pluraltest4.py', 'pluraltest5.py', 'pluraltest6.py', 'romantest1.py', 'romantest10.py', 'romantest2.py', 'romantest3.py', 'romantest4.py', 'romantest5.py', 'romantest6.py', 'romantest7.py', 'romantest8.py', 'romantest9.py']
glob
modul egy helyettesítő karaktert tartalmazó karakterláncot vár, és visszaadja az arra illeszkedő összes fájl és könyvtár elérési útját. Ebben a példában a karakterlánc a könyvtár elérési útja, plusz a „*.xml
”, amely az examples
alkönyvtárban lévő összes .xml
fájlra illeszkedik.
examples
alkönyvtárra. Az os.chdir()
függvény elfogad relatív útvonalneveket is.
.py
kiterjesztésű, és atest
szót a fájlnevükben bárhol tartalmazó fájlját.
Minden korszerű fájlrendszer tárol metaadatokat a fájlokról: létrehozás dátuma, legutóbbi módosítás dátuma, fájlméret és így tovább. A Python egységes API-t biztosít ezen metaadatok eléréséhez. A fájlt nem kell megnyitnod, csak a fájlnévre van szükséged.
>>> import os >>> print(os.getcwd()) ① c:\Users\pilgrim\diveintopython3\examples >>> metadata = os.stat('feed.xml') ② >>> metadata.st_mtime ③ 1247520344.9537716 >>> import time ④ >>> time.localtime(metadata.st_mtime) ⑤ time.struct_time(tm_year=2009, tm_mon=7, tm_mday=13, tm_hour=17, tm_min=25, tm_sec=44, tm_wday=0, tm_yday=194, tm_isdst=1)
examples
mappa.
feed.xml
egy fájl az examples
mappában. Az os.stat()
függvény hívása egy olyan objektumot ad vissza, amely számos különböző típusú metaadatot tartalmaz a fájlról.
st_mtime
a módosítás ideje, de egy nem különösebben hasznos formátumban van. (Technikailag ez az Epoch, azaz 1970. január 1. első másodperce óta eltelt másodpercek száma. Komolyan.)
time
modul a szabványos Python függvénytár része. A különböző időábrázolások közti átalakításhoz, az időértékek karakterláncokká formázásához és az időzónák kezeléséhez használható függvényeket tartalmaz.
time.localtime()
függvény átalakít egy időértéket az Epoch-óta-eltelt-másodpercek formátumról (ezt az os.stat()
függvény által visszaadott st_mtime
tulajdonság tartalmazza) a sokkal hasznosabb év, hónap, nap, óra, perc, másodperc stb. formátumra. Ezt a fájlt utoljára 2009. július 13-án, délután 5.25 körül módosították utoljára.
# az előző példa folytatása >>> metadata.st_size ① 3070 >>> import humansize >>> humansize.approximate_size(metadata.st_size) ② '3.0 KiB'
os.stat()
függvény visszaadja a fájl méretét is, az st_size
tulajdonságban. A feed.xml
fájl 3070
bájt.
st_size
tulajdonságot átadhatod az approximate_size()
függvénynek.
Az előző szakaszban a glob.glob()
függvény relatív útvonalnevek listáját adta vissza. Az első példa olyan útvonalneveket tartalmaz, mint az 'examples\feed.xml'
, és a második példa még rövidebbeket, mint a 'romantest1.py'
. Amíg ugyanabban az aktuális munkakönyvtárban maradsz, ezek a relatív útvonalnevek működni fognak a fájlok megnyitásakor vagy a metaadatok lekérésekor. Ha azonban abszolút – azaz a könyvtárneveket egészen a gyökérkönyvtárig vagy a meghajtó betűjelig tartalmazó – útvonalnevet szeretnél összeállítani, akkor az os.path.realpath()
függvényt kell használnod.
>>> import os >>> print(os.getcwd()) c:\Users\pilgrim\diveintopython3\examples >>> print(os.path.realpath('feed.xml')) c:\Users\pilgrim\diveintopython3\examples\feed.xml
⁂
A listafeldolgozó használatával tömören le lehet képezni egy listát egy másik listára egy függvény alkalmazásával a lista minden egyes elemére.
>>> a_list = [1, 9, 8, 4] >>> [elem * 2 for elem in a_list] ① [2, 18, 16, 8] >>> a_list ② [1, 9, 8, 4] >>> a_list = [elem * 2 for elem in a_list] ③ >>> a_list [2, 18, 16, 8]
elem * 2
függvényt, és az eredményt hozzáfűzi a visszaadott listához.
Listafeldolgozóban tetszőleges Python kifejezést használhatsz, beleértve az os
modul fájlok és könyvtárak kezelésére szolgáló függvényeit is.
>>> import os, glob >>> glob.glob('*.xml') ① ['feed-broken.xml', 'feed-ns0.xml', 'feed.xml'] >>> [os.path.realpath(f) for f in glob.glob('*.xml')] ② ['c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml', 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml', 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml']
.xml
fájl listáját.
.xml
fájlok előbbi listáját, és átalakítja azt teljes útvonalnevek listájává.
A listafeldolgozók szűrni is tudják az elemeket, ezáltal az eredeti listánál rövidebb eredményt előállítva.
>>> import os, glob
>>> [f for f in glob.glob('*.py') if os.stat(f).st_size > 6000] ①
['pluraltest6.py',
'romantest10.py',
'romantest6.py',
'romantest7.py',
'romantest8.py',
'romantest9.py']
if
kifejezést a listafeldolgozó végére. Az if
kulcsszó utáni kifejezés kiértékelődik a lista minden egyes elemére. Ha a kifejezés True
-ra értékelődik ki, akkor a kimenet tartalmazni fogja az elemet. Ez a listafeldolgozó megnézi az aktuális könyvtár összes .py
fájlját, és az if
kifejezés annak alapján szűri ezt a listát, hogy az egyes fájlok mérete nagyobb-e 6000
bájtnál. Hat ilyen fájl van, így a listafeldolgozó hat fájlnévből álló listát ad vissza.
A listafeldolgozók az eddigi példákban egyszerű kifejezéseket mutattak be – egy szám konstanssal szorzása, egy függvény hívása, vagy egyszerűen az eredeti listaelem visszaadása (szűrés után). De a listafeldolgozó összetettségének nincs korlátja.
>>> import os, glob >>> [(os.stat(f).st_size, os.path.realpath(f)) for f in glob.glob('*.xml')] ① [(3074, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-broken.xml'), (3386, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed-ns0.xml'), (3070, 'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml')] >>> import humansize >>> [(humansize.approximate_size(os.stat(f).st_size), f) for f in glob.glob('*.xml')] ② [('3.0 KiB', 'feed-broken.xml'), ('3.3 KiB', 'feed-ns0.xml'), ('3.0 KiB', 'feed.xml')]
.xml
fájlt az aktuális munkakönyvtárban, lekéri az egyes fájlok méretét (az os.stat()
függvény hívásával), és összeállít egy tuple-t a fájlok méretéből és az abszolút elérési útjukból (az os.path.realpath()
függvény hívásával).
approximate_size()
függvényt az egyes .xml
fájlok fájlméretével.
⁂
A szótárfeldolgozó olyan, mint a listafeldolgozó, csak lista helyett szótárat állít elő.
>>> import os, glob >>> metadata = [(f, os.stat(f)) for f in glob.glob('*test*.py')] ① >>> metadata[0] ② ('alphameticstest.py', nt.stat_result(st_mode=33206, st_ino=0, st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=2509, st_atime=1247520344, st_mtime=1247520344, st_ctime=1247520344)) >>> metadata_dict = {f:os.stat(f) for f in glob.glob('*test*.py')} ③ >>> type(metadata_dict) ④ <class 'dict'> >>> list(metadata_dict.keys()) ⑤ ['romantest8.py', 'pluraltest1.py', 'pluraltest2.py', 'pluraltest5.py', 'pluraltest6.py', 'romantest7.py', 'romantest10.py', 'romantest4.py', 'romantest9.py', 'pluraltest3.py', 'romantest1.py', 'romantest2.py', 'romantest3.py', 'romantest5.py', 'romantest6.py', 'alphameticstest.py', 'pluraltest4.py'] >>> metadata_dict['alphameticstest.py'].st_size ⑥ 2509
.py
fájlt, amelyek neve tartalmazza a test
szót, majd összeállít egy tuple-t a fájlok neveiből és metaadataiból (amelyeket az os.stat()
függvény ad vissza).
f
) a szótár kulcsa, a kettőspont utáni kifejezés (ebben a példában az os.stat(f)
) az érték.
glob.glob('*test*.py')
által visszaadott fájlnevek.
os.stat()
függvény visszatérési értéke. Ez azt jelenti, hogy egy fájl a neve alapján „kikereshető” a szótárból a metaadatainak eléréséhez. Az egyik ilyen metaadat az st_size
, a fájlméret. Az alphameticstest.py
fájl 2509
bájt hosszú.
A listafeldolgozóhoz hasonlóan a szótárfeldolgozóhoz hozzáadhatsz egy if
kifejezést a bemeneti sorozat szűréséhez egy minden elemmel együtt kiértékelésre kerülő kifejezéssel.
>>> import os, glob, humansize >>> metadata_dict = {f:os.stat(f) for f in glob.glob('*')} ① >>> humansize_dict = {os.path.splitext(f)[0]:humansize.approximate_size(meta.st_size) \ ... for f, meta in metadata_dict.items() if meta.st_size > 6000} ② >>> list(humansize_dict.keys()) ③ ['romantest9', 'romantest8', 'romantest7', 'romantest6', 'romantest10', 'pluraltest6'] >>> humansize_dict['romantest9'] ④ '6.5 KiB'
glob.glob('*')
), lekéri az egyes fájlok metaadatait (os.stat(f)
), és összeállít egy szótárat, amelynek kulcsai a fájlnevek, értékei pedig az egyes fájlok metaadatai.
6000
bájtnál nagyobb fájlokat (if meta.st_size > 6000
), és a kapott listát használja egy szótár összeállítására, amelynek kulcsai a kiterjesztés nélküli fájlnevek (os.path.splitext(f)[0]
), értékei pedig az egyes fájlok becsült méretei (humansize.approximate_size(meta.st_size)
).
approximate_size()
függvény által visszaadott karakterlánc.
Itt egy szótárfeldolgozókkal kapcsolatos trükk, ami egyszer még jól jöhet: egy szótár kulcsainak és értékeinek felcserélése.
>>> a_dict = {'a': 1, 'b': 2, 'c': 3} >>> {érték:kulcs for kulcs, érték in a_dict.items()} {1: 'a', 2: 'b', 3: 'c'}
Természetesen ez csak akkor működik, ha a szótár értékei megváltoztathatatlanok, például karakterláncok vagy tuple-k. Ha ezt egy listákat tartalmazó szótárral próbálod meg, az látványosan nem fog sikerülni.
>>> a_dict = {'a': [1, 2, 3], 'b': 4, 'c': 5} >>> {érték:kulcs for kulcs, érték in a_dict.items()} Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <dictcomp> TypeError: unhashable type: 'list'
⁂
Ne hagyjuk ki a halmazokat se, ezeknek is van saját feldolgozószintaxisuk, amely nagyon hasonlít a szótárfeldolgozók szintaxisához. Az egyetlen különbség, hogy a halmazok csak értékeket tartalmaznak kulcs:érték párok helyett.
>>> a_set = set(range(10)) >>> a_set {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} >>> {x ** 2 for x in a_set} ① {0, 1, 4, 81, 64, 9, 16, 49, 25, 36} >>> {x for x in a_set if x % 2 == 0} ② {0, 8, 2, 4, 6} >>> {2**x for x in range(10)} ③ {32, 1, 2, 4, 8, 64, 128, 256, 16, 512}
0
és 9
közti számok négyzetét.
if
kifejezést az egyes elemek szűréséhez azok visszaadása előtt az eredmény halmazba.
⁂
os
modul
os
– Portable access to operating system specific features
os.path
modul
os.path
– Platform-independent manipulation of file names
glob
modul
glob
– Filename pattern matching
time
modul
time
– Functions for manipulating clock time
© 2001–11 Mark Pilgrim