S moduly jsme se nejviditelněji už několikrát setkali v souvislosti s příkazem import
:
Tento slouží k zavedení jakýchsi „doplňkových“, do té doby nedostupných částí jazyka (knihoven, konstant, funkcí...), do aktuálního pracovního paměťového prostoru. Co však moduly konkrétně jsou?
Ve skutečnosti je modulem každý soubor *.py. V jeho rámci je společná globální úroveň viditelnosti pro proměnné, funkce apod. v něm definované.
Skutečná struktura modulu však může (a samozřejmě často bývá) mnohem složitější – obsahuje nejen více souborů, ale většinou jsou tyto soubory i uspořádány do hierarchické struktury vhodně pojmenovaných adresářů. Takovýmto konstruktům se pak říká balíčky (packages).
Modul v nejjednodušším případě importujeme pod jeho jménem. Pak můžeme používat v něm definované objekty (funkce, proměnné atd.) pomocí tečkové notace, tedy jako členy příslušného jmenného prostoru.
Přitom obsahuje-li modul proveditelné příkazy, ty se provedou při jeho prvním importu. Typicky tak slouží třeba k jeho inicializaci a podobně (pro pěknou ukázku se podívejte na implementaci modulu html.entities).
del MODUL
, ale kromě vyčištění globálního prostoru jmen (namespace) nic nezískáte – v rámci stejného běhu interpretru při příštím importu totiž (z důvodů efektivity) „obživne“ předchozí použitý modul, nikoli jeho případná novější verze. Skutečné znovunačtení modulu řeší metoda reload(MODUL)
z balíčku importlib (pro Python 3.3 a nižší se nachází v balíčku imp), ale i ta má své mouchy (např. stará jména nepřítomná v novém modulu jsou zachována).
Moduly a objekty z nich můžete naimportovat a používat mnoha různými způsoby:
Přitom na všechny importy se vztahuje jedno základní pravidlo:
Poslední naimportovavší se objekt daného jména „schová“ všechny předchozí se stejným jménem.
A to se samozřejmě vztahuje i na systémové objekty (ostatně trik s předefinováním významu například funkce print jsem určitě už někdy zmiňoval).
Zatímco varianty importu z předchozího slajdu importují pouze vybrané moduly či jejich prvky, existuje také způsob, jak naimportovat z modulu všechna (na globální úrovni modulu) viditelná jména – from MODUL import *
. Má to nepřekvapivě „drobný“ zádrhel – všechna tato jména se stanou součástí globálního jmenného prostoru skriptu, do nějž byla naimportována. Se všemi průšvihy (jako je kupříkladu zastínění existujících jmen apod.) z toho vyplývajícími.
Vzhledem k uvedenému problému se tudíž rozsah exportovaných jmen dá řídit magickou proměnnou modulu __all__
. To je obyčejný seznam jmen objektů (v podobě řetězců), které chceme exportovat.
Jeho funkcionalita je natolik promyšlená, že pokud napíšete __all__ = []
, nevyexportuje se pomocí hvězdičky vůbec nic.
Očekáváte-li tudíž, že by váš skript mohl někdo (třeba i vy sami) použít jako modul, je doplnění magické proměnné __all__
víc než dobrý nápad.
Jelikož modul jakožto soubor se importuje pod jménem souboru bez přípony, asi nepřekvapí, že na jeho jméno jsou kladeny stejné požadavky jako na jména objektů v programu obecně.
Tudíž jména typu 1.py a podobná se přímo naimportovat skutečně nepodaří.
Odpověď na tuto otázku je zdánlivě jednoduchá – v adresářích, které jsou vyjmenované v proměnné sys.path
:
Praxe je však podstatně složitější, jak uvidíme na dalších slajdech.
Sama proměnná sys.path se skládá z několika částí, které jsou při dotazu na import modulu/balíčku postupně prohledávány jedna po druhé:
Povšimněte si především následujícího:
Aktuální adresář je na prvním místě, z čehož vyplývá, že moduly vhodných jmen v aktuálním adresáři zvítězí nad vším ostatním.
V důsledku struktury proměnné sys.path je možné standardní pythoní distribuci rozšířit o další místa výskytu balíčků/modulů několika různými způsoby:
PYTHONPATH
.
site.USER_SITE
. Nemusí existovat (a příkaz python -m site
vám to nahlásí) – pak ho stačí vytvořit.
Jelikož proměnná sys.path je obyčejný seznam, můžete si ji upravit přidáním vlastních cest na vhodná místa. Má to ale drobný zádrhel – budou-li cesty relativní vůči umístění hlavního skriptu, nebude to fungovat v případě skriptů spouštěných relativně k místu volání (tj. například jako python3 cesta/skript.py
). Oprava je (teoreticky) jednoduchá – stačí cesty uvést absolutně.
sys.path
.
Úspěšně naimportované moduly jsou Python'em zkompilovány do bajtkódu interpretru, a to buď do souborů *.pyc
(standardně) nebo *.pyo
(byl-li interpretr spuštěn s parametrem pro optimalizaci). Jejich (víceméně jedinou) výhodou je, že se rychleji načítají (není třeba znovu procházet zdrojový kód programu, již je připraven v binární podobě pro virtuální počítač Python'u) => při příštím použití budou dříve k dispozici.
Python pozná, může-li použít některý ze zkompilovaných souborů nebo byl-li jejich zdrojový *.py
soubor mezitím modifikován.
*.pyo vzniknou vyvoláním interpretru s přepínačem -O
. V dané době to znamená pouze jediné – z kódu jsou odstraněny všechny příkazy assert
.
-OO
pošle dopryč i komentářové řetězce, což už může pochopitelně způsobit havárii některých programů.
Jelikož vlastně každý pythonovský zdrojový soubor může sloužit zároveň jako modul i jako skript, potřebujeme mezi těmito použitími rozlišit. K tomu slouží globální proměnná aktuálního modulu/skriptu __name__
, která obsahuje:
__main__
– v případě skriptu; tj. pokud jsme soubor *.py spustili jako „hlavní“, např. z příkazové řádky
import SOUBOR
Chceme-li tedy, aby se modul při použití jako skript (tj. tehdy, když není importován) nějak rozumně choval, stačí na jeho konec přidat kód:
Python má zabudovanou ochranu proti cyklickým importům. Pokusíte-li se například naimportovat „sami sebe“, vypíše se nápis Ahoj! pouze dvakrát:
Zároveň si všimněte, jaký je postupně obsah proměnné __name__
:
import kruhem
.
import kruhem
, tentokráte z importovaného modulu, se již neprovede, protože Python pozná, že se jedná o kruhový import.
print()
z importovaného modulu, takže proměnná __name__
bude obsahovat jméno tohoto modulu (tedy kruhem).
print()
. Nepřekvapivě tedy proměnná __name__
nyní vrátí řetězec __main__.
PS: Ale už jsem na vlastní oči viděl tuto kontrolu selhat – sice se nic neucyklilo, ale volaly se věci, které neměly :-)
Dejte si pozor, abyste:
Celkem snadno se vám může stát, že se v importech tak říkajíc utopíte a nebudete mít ani nejmenší páru, který sakra modul se nakonec vlastně používá :-) K záchraně pak – alespoň v jednodušších případech – může posloužit speciální proměnná importovaného modulu __file__
, která nahlásí, odkud v rámci souborového systému inkrimimovaný pachatel pochází:
Definovat všechny potřebné objekty v rámci jednoho modulu je při čemkoliv jen trošku větším velmi nešikovné. Python má pro tento případ k dispozici velmi jednoduchý a elegantní „balíčkovací“ mechanizmus:
Rozdělte objekty do jednotlivých modulů a ty do jednotlivých podadresářů hlavního adresáře (tj. balíčku, package) podle jejich zaměření a hierarchie.
Jednotlivé moduly a jejich objekty jsou pak dostupné pomocí klasické tečkové notace, a to buď absolutně nebo relativně (viz následující příklad).
Princip popsaný na předchozím slajdu funguje v Python'u po celou jeho historii až do verze 3.2 a má své plusy i mínusy:
Z tohoto a mnoha jiných důvodů byl systém vyhledávání modulů v balíčcích a náležení do balíčků v Python'u 3.3 docela radikálně změněn. Nejdříve se podíváme na ten starší, a pak si okomentujeme ten novější.
Moduly z balíčku se pak importují naprosto standardně a průhledně (a absolutně), například..
..a podobně.
Na posledním místě importu tedy může být buď modul nebo submodul (tedy adresář, podadresář nebo soubor; pro případ import MODUL
) nebo nějaký jeho objekt (funkce, proměnná…; pro případ from MODUL import OBJEKT
).
Podobným způsobem na sebe mohou odkazovat moduly uvnitř balíčku. Jde to buď absolutně (tj. od kořene balíčku)..
..nebo relativně (tj. vůči aktuální pozici):
from .MODULE import NĚCO
.
Soubor __init__.py
je vyžadován, aby byl adresář rozpoznán jako balíček. Je to mimo jiné z toho důvodu, aby se náhodným importem čehokoliv nezastínily například systémové balíčky Python'u (obecně balíčky na pozdějších místech sys.path).
Úplně stačí, aby soubor __init__.py byl prázdný. Jelikož je ale čten (a vykonán) při importu balíčku (tedy právě jednou), může s výhodou obsahovat například jeho inicializační kód (je-li nějaký potřeba).
Jednoduchý balíček tak bude vypadat přibližně takto:
Dostupné moduly (tedy soubory *.py a další) naimportovaného balíčku (tedy adresáře obsahujícího soubor __init__.py) však v rámci výpisu funkce dir()
nejsou vidět. A to přesto, že je můžete přímo naimportovat (pokud víte o jejich existenci):
Chcete-li, aby byly takto vidět, stačí je v rámci souboru __init__.py naimportovat následujícím způsobem:
Vzhledem k možné velmi komplikované struktuře balíčků se nedalo zabránit tomu, aby lidé neměli tendenci spouštět z příkazové řádky moduly kdesi uvnitř této struktury. Jenže to je problém:
Modul spuštěný přímo interpretrem není součástí balíčku. Takže mimo jiné přestanou fungovat relativní importy uvnitř balíčku.
Specificky – atribut __name__ takto spuštěného modulu obsahuje řetězec "__main__" a nikoli identifikaci modulu/balíčku, kterou by obsahoval, kdyby byl normálně naimportován. To se dá obejít dvěma způsoby:
python -m CESTA_K_MODULU/modul.py
.
__package__
.
Druhá varianta má tu výhodu, že si nemusíte pamatovat, že soubory z balíčků máte volat jiným způsobem. Navíc pro importované balíčky/moduly je proměnná __package__ automaticky doplňována (což vám sice nepomůže při volání, ale uvnitř kódu se pak náležení k jednotlivým balíčkům snáze kontroluje).
Mezi řadami Python'u 2.x a 3.x je kromě kosmetického rozdílu v místě generování bajtkódových souborů – stejný adresář pro řadu 2.x a speciální podadresář __pychache__
pro řadu 3.x – jeden veliký, převeliký rozdíl:
Bajtově zkompilované soubory z podadresáře __pycache__
nebudou naimportovány, pokud k nim bude (o úroveň výš) chybět odpovídající zdrojový kód v podobě *.py.
Prakticky to pro trojkový Python znamená, že:
PS: Od verze Python'u 3.8 si navíc můžete vybrat, kam se budou moduly předkompilovávat (viz proměnná prostředí PYTHONPYCACHEPREFIX, parametr spuštění interpretu -X pycache_prefix=PATH
a info z běžícího programu sys.pycache_prefix
). Což je výhodné, potřebujete-li vlastní zdrojové soubory z libovolného důvodu umístit třebas na jiném svazku.
Jak už jsem zmiňoval, Python 3.3 značně změnil způsob zacházení s balíčky. Starší systém, který jsme si právě vysvětlili, můžete vesele dál používat, ale ponovu můžete jednotlivé balíčky také označit jmenným prostorem (a vytvořit tak tzv. namespace package), který pak – mimo jiné – umožňuje skládat dohromady balíčky ne pouze podle přítomnosti ve stejném nadřazeném adresáři (tedy na stejné cestě), ale především pak podle přítomnosti ve stejném jmenném prostoru.
Principielně je přiřazení balíčku pod příslušný jmenný prostor zařízeno neuvedením souboru __init__.py v adresáři balíčku – odpovídající zařazení balíčku je přímo určeno jeho rodičovským adresářem, který se musí nacházet v některé z cest zanesených v seznamu sys.path
.
__file__
ke konkrétnímu modulu naopak (read-only) iterátor __path__
přes seznam cest, které všechny obsahují daný jmenný prostor.
Oba balíčkovací systémy existují vedle sebe a vzájemně se doplňují – platí totiž priorita běžných balíčků:
Běžný balíček (tedy obsahující __init__.py) vždy zvítězí nad NS-balíčkem stejného jména.
Prakticky to znamená, že:
sys.path
);
Kromě toho se ještě běžné balíčky rychleji importují, byť to již není tak významný rozdíl:
Pokud v souboru work.py rozšíříme seznam v proměnné sys.path o cesty k adresářům rodic1
a/nebo rodic2
(například klidně i relativně* jako sys.path += ['../rodic2']
), můžeme pak použít klasické importy typu:
Výhodou je, že daný NS-balíček balicek nemusíme zavádět vždy celý najednou, ale postupně po kouscích (a celý třeba i nikdy).
Jako přítomnost souboru __init__.py
zajišťuje, že je balíček vůbec pokládán za balíček (alespoň ve většině případů a verzí jazyka), tak přítomnost souboru __main__.py
zase zajišťuje, že je balíček pokládán za spustitelný z příkazové řádky pod svým jménem.
Funkcionalita při volání je podobná, jako u __init__.py při importování – při zavolání interpretru na takto „vybavený“ adresář bude vykonán kód, který je napsán uvnitř souboru __main__.py.
Mějme kupříkladu balíček foo s následující strukturou:
Přitom soubor __main__.py obsahuje..
..a soubor bar.py zase:
Budeme-li stát na úrovni adresáře (a tedy balíčku) foo, příslušná volání budou mít následující efekty:
Celé to jde dokonce ještě dál – z adresáře obsahujícího soubor __main__.py
můžete vyrobit zip-archív (resp. ze souborů v něm obsažených; adresář bude efektivně nahrazen archívem)..
..a chovat se k němu stejně, jako kdyby to byl adresář:
#
, prázdné řádky a řádky obsahující pouze bílé znaky) a soubor pak můžete na unixových systémech skutečně rovnou spouštět. Jen nezapomeňte, že konce řádků přidaného textu musí být unixové (tedy LF).
Vyhledávání metod (v rámci balíčku) je vyhodnocováno za běhu. Proto bude (o něco málo) rychlejší, ale hlavně asi také čitelnější, připravit si na podmodul alias (verze vpravo):
Budete-li distribuovat program ve formě zip-archivu, nezapomeňte, že Python při importu modulů z něj nebude provádět jejich předkompilaci do pyc-souborů (prostě nebude měnit obsah archívu), takže pro krátkoběžící programy může být čas vykonání o něco delší (bude totiž ještě potřeba provést jejich kompilaci v paměti).
Pro ukázání „obsahu“ modulu, tj. objektů dostupných v rámci příslušného modulu (ať už jsou to podmoduly, funkce, proměnné či třeba třídy), slouží globální funkce dir()
:
dir(MODUL)
– vrátí seřazený seznam řetězců představujících jména objektů dostupných v modulu MODUL
dir()
– vypíše jména aktuálně definovaných objektů (tj. například v rámci daného sezení)
dir()
není nijak přesně definován – prostě se pokusí pokaždé něco vrátit, ale jestli je to všechno, případně co to „všechno“ vlastně znamená, není nijak zaručené ani předepsané.
Příklad čerstvě spuštěného pythoního shellu:
Jelikož dir() vrací seznam podobjektů v podobě jejich jmen, tedy řetězců, musíme se ke konkrétnímu objektu dostat nějak jinak. K tomu slouží globální funkce getattr():
Příklad (zjištění parametrů, s jakými byl spuštěn aktuální skript):
PS: Použití getattr() v tomto případě je samozřejmě poměrně umělé. Jeho hlavní výhoda tkví v tom, že dokáže získat odkaz na atribut objektu i v případě, že jméno tohoto atributu není validní pythoní identifikátor, což se často hodí např. u přístupu k funkcím v externích knihovnách (jejichž jméno je totiž často zamanglováno či jinak upraveno).