Souborový systém

Pár rad, jak pracovat se souborovým systémem v Pythonu.

Ukázky pomocí staršího, ale stále podporovaného a používaného rozhraní pro práci se souborovým systémem budou uvedeny v levém sloupci. Toto rozhraní má také tu výhodu, že je podobné rozhraní pro práci se soubory v jazyku C a dalších.

Ukázky pomocí novějšího rozhraní, které nabízí modul pathlib, budou uvedeny v pravém sloupci. Rozhraní je modernější a mnoha lidem se s ním pracuje líp.

Práce s adresáři

Pravděpodobně budete chtít ve svých skriptech nějaká data načítat ze souborů nebo ukládat do souborů. Velmi často budete mít v jedné proměnné, např. fname, uloženo jméno souboru a v druhé proměnné, např. fpath, cestu, kde se má soubor nacházet. Abyste k souboru mohli přistoupit, musíte fpath a fname spojit správným oddělovačem, který se používá v OS, na němž právě pracujete. Na Unixech a Linuxech se jako oddělovač používá normální lomítko /, na Windows zpětné lomítko \, které se ovšem musí v Pythonských řetězcích zapisovat jako \\ (protože \ se používá k uvození speciálních znaků). V praxi si ovšem s detekcí operačního systému nemusíte dělat starosti, použijete-li vhodné nástroje.

Při použití funkcí z modulů os a os.path budou jednotlivé cesty a názvy souborů reprezentované prostými řetězci. Proměnná os.sep obsahuje ten správný separátor, ať už jste na kterémkoli OS. Takže následující kód

>>> import os
>>> fpath = 'cesta_k_souboru'
>>> fname = 'nazev_souboru'
>>> fpath + os.sep + fname
bude mít na Windows výsledek
'cesta_k_souboru\\nazev_souboru'
zatímco na Linuxu
'cesta_k_souboru/nazev_souboru'

Můžeme také využít funkce os.path.join(). Příkaz

>>> os.path.join(fpath, fname)
bude mít na Windows i na Linuxu stejný výsledek, jako předchozí varianta využívající os.sep. Navíc se tato funkce také vypořádá s případem, kdy fpath už končí oddělovačem (tj. nezdvojí ho).

Při použití tříd a funkcí z modulu pathlib budou jednotlivé cesty reprezentované instancemi tříd PosixPath (Linux, Mac) nebo WindowsPath. Instanci správného typu ale v kódu vytvoříte pomocí třídy Path. Tyto třídy mají přetížený operátor /, který se používá ke spojování jednotlivých částí cest pomocí správného oddělovače. Takže následující kód

>>> from pathlib import Path
>>> fpath = Path('cesta_k_souboru')
>>> fname = 'nazev_souboru'
>>> fpath / fname
bude mít na Windows výsledek
WindowsPath('cesta_k_souboru/nazev_souboru')
zatímco na Linuxu
PosixPath('cesta_k_souboru/nazev_souboru')

Můžeme také využít metodu .joinpath(). Příkaz

>>> fpath.joinpath(fname)
bude mít na Windows i na Linuxu stejný výsledek, jako předchozí varianta využívající operátor /. Navíc se tato funkce také vypořádá s případem, kdy fpath už končí oddělovačem (tj. nezdvojí ho).

Uložení souboru do aktuálního adresáře

Váš program si bude chtít uložit nějaká data pro svou potřebu a pozdější využití. Logické umístění souboru s takovými daty je adresář, v němž se nachází váš program (příp. jeho podadresáře). Pravděpodobně vás napadne použít relativní cestu, díky níž se umístění souboru odvozuje od aktuálního adresáře. Pokud tedy z příkazové řádky, či z Pythonského interpretu spustíte svůj skript script.py, v němž zavoláte příkaz

f = open('data.txt', 'wt', encoding='utf-8')
opravdu se vedle vašeho skriptu script.py vytvoří ve stejném adresáři datový soubor data.txt. A to jsme přece chtěli, ne?

Problém nastává v okamžiku, kdy se váš skript script.py stane modulem, který importujete do dalších modulů a ty jej využívají. Pokud váš modul script.py importuje spuštěný skript z vyšší úrovně (“z nadřazeného adresáře”), není již aktuálním adresářem ten adresář, který obsahuje script.py, ale adresář, v němž sídlí ten skript, který byl spuštěn Pythonským interpretem. Soubor data.txt by byl vytvořen někde jinde - daleko od zamýšleného umístění.

Jak lze zajistit, aby se datový soubor vždy ukládal tam, kam chceme, tedy do stejného adresáře, kde je umístěn i náš script.py?

Pokud se soubor importuje jako modul, lze cestu k němu získat konstrukcí

fpath = os.path.dirname( os.path.abspath(__file__) ) 
Tato konstrukce však může někdy selhat (pokud např. budeme modul spouštět položkou Run z editoru IDLE, nebo pokud jej v Pythonu 2 budeme spouštět funkcí execfile() - v takovém případě totiž nedochází k importu modulu a nenastavuje se proměnná file).

Zcela univerzální postup představuje následující konstrukce využívající modul inspect:

import inspect
fpath = os.path.dirname( inspect.getfile( inspect.currentframe() ) )

courses/b4b33rph/tutorialy/python/filesystem.txt · Last modified: 2024/10/09 10:29 by xposik