Hlavní navigace

Létající cirkus (5)

28. 2. 2002
Doba čtení: 8 minut

Sdílet

Pryč jsou doby, kdy se zdrojové kódy programů psaly do jednoho souboru bez ladu a skladu, pryč jsou časy, kdy nejsložitějsí programy měly pár stovek řádek. V dnešní době, kdy je kladen velký důraz na opakované použití kódu a na programy složené z komponent, je třeba program rozdělit do několika částí. V jazyce Python jde o moduly a balíčky modulů a další pokračování tohoto seriálu bude právě o nich.

Příklad

Jak jsme si již řekli v jedné z prvních částí našeho seriálu, program v Pythonu nemusíme psát přímo v prostředí interaktivního interpretu, ale lze ho zapsat i do zdrojového souboru, a ten pak dále spouštět. V dalším výkladu budeme dále pracovat s tímto zdrojovým souborem (module1.py):

#!/usr/bin/env python                               # (1)
'Ukazkovy modul v jazyce Python'                    # (2)
import sys                                          # (3)
import os                                           # (4)
                                                    # (5)
default_path = os.environ['PATH']                   # (6)
                                                    # (7)
def print_environment(env = None):                  # (8)
    '''Vytiskne promenne prostredi v poli env,      # (9)
    neni-li zadano, uvazuje os.environ'''           # (10)
    env = env or os.environ                         # (11)
    for var in env.keys():                          # (12)
        print '$' + var + '=' + env[var]            # (13)
                                                    # (14)
def list_path(path = None):                         # (15)
    '''Vrati seznam vsech souboru v ceste path,     # (16)
    neni-li zadano, uvazuje globalni promennou      # (17)
    default_path'''                                 # (18)
    path = path or default_path                     # (19)
    path_l = path.split(':')                        # (20)
    retval = []                                     # (21)
    for el in path_l:                               # (22)
        try:                                        # (23)
            retval.extend(os.listdir(el))           # (24)
        except OSError:                             # (25)
            pass                                    # (26)
    return retval                                   # (27)
                                                    # (28)
if __name__ == '__main__':                          # (29)
    my_env = {'PATH': os.environ['PATH']}           # (30)
    print_environment(my_env)                       # (31)
    print_environment()                             # (32)
    print list_path()[:50]                          # (33)

Takto nějak může vypadat modul v jazyce Python. Na prvním řádku může být magická sekvence pro spuštění modulu přímo v pythonu, má-li soubor nastaven executable bit. Následuje importování dvou modulů sys a os. Dále definujeme jednu globální proměnnou (globální v rámci tohoto modulu) a nakonec definujeme pár funkcí, které náš modul bude poskytovat. Výše uvedený soubor module1.py uložíme do libovolného pracovního adresáře a v tomto adresáři si spustíme interaktivní interpret příkazem python:

honza@localhost ~/tmp $ python
Python 2.0 (#1, Apr 11 2001, 19:18:08)
[GCC 2.96 20000731 (Linux-Mandrake 8.0)] on linux-i386
Type "copyright", "credits" or "license" for more information.
>>> import module1                                  # (1)
>>> dir()                                           # (2)
['__builtins__', '__doc__', '__name__', 'module1']
>>> dir(module1)                                    # (3)
['__builtins__', '__doc__', '__file__',
'__name__', 'default_path', 'list_path', 'os',
'print_environment', 'sys']
>>> module1.default_path                            # (4)
'/usr/bin:/bin:/usr/X11R6/bin:/usr/local/bin'

Každý soubor se zdrojovým kódem v jazyce Python a s příponou .py může zároveň být i modulem. Zavedení modulu (importování) se provádí (překvapivě :) konstrukcí import (řádek 1), která může mít mnoho tvarů. V každém případě ale import vytváří nový záznam (záznamy) v aktuálním lokálním prostoru jmen, jak ukazuje volání funkce dir() na řádku (2).

Funkce dir()

Je vestavěnou funkcí, čili nemusí se volat plně kvalifikovaným jménem (tj. __builtin__.dir). Funkce dir() volaná bez parametrů vrátí seznam všech proměnných v lokálním a globálním prostoru jmen. Předáme-li jí jeden parametr, vrátí dir() seznam všech atributů parametru, viz řádek (3) předchozího příkladu.

Tečková notace

Pro přístup k atributům libovolného objektu se v Pythonu používá tečková notace. Zápis os.path tedy znamená atribut ‚path‘ objektu ‚os‘. Atributy mohou mít samozřejmě další atributy, například os.path.split.

Moduly

Importováním modulu vznikne nový objekt typu module, jehož atributy jsou proměnné v globálním prostoru jmen tohoto modulu. K těmto atributům se samozřejmě přistupuje pomocí tečkové notace. To můžeme vidět i na řádku (4) výše uvedeného příkladu.

Každý modul má kromě těch atributů, které mu přiřadil jeho tvůrce, i atributy, které mu dal do vínku interpret Pythonu. Jsou to hlavně:

Tabulka č. 254
__builtins__ asociativní pole, které má význam builtin prostoru jmen modulu
__doc__ dokumentační řetězec modulu
__file__ jméno souboru, ze kterého byl modul zaveden
__name__ jméno modulu

Dokumentační řetězce

Každý modulu (a nejen modul, ale i třída, její metoda a libovolná funkce) má speciální atribut se jménem __doc__. Ten obsahuje takzvaný dokumentační řetězec, (řádky 2, 9–10 a 16–18 souboru example1.py), ve kterém může být zapsán komentář k modulu (třídě, metodě). Zapisuje se jako obyčejný řetězec hned na začátek souboru před vše ostatní (podobně následuje hned za definicí třídy nebo metody). Tyto komentáře jsou nepovinnou součástí definice, je ale zvykem je psát, neboť velice usnadňují práci v interaktivním interpretu.

Konstrukce import

Konstrukce import je snad nejvariabilnější konstrukcí v Pythonu. Následuje několik ukázek použití, pro kompletní přehled možných kombinací vám doporučuji referenční příručku jazyka.

>>> import module1                                  # (1)
>>> import module1 as m                             # (2)
>>> import module1, os as opersys                   # (3)
>>> from module1 import default_path, \             # (4)
... print_environment as pe                         # (5)
>>> from module1 import *                           # (6)

Konstrukce import nejprve najde modul, který je třeba importovat, poté spustí jeho tělo. Tím se definují globální proměnné (viz. proměnná default_path v module1) a také další objekty, které bude modul obsahovat (funkce print_environment, list_path). Takto se spustí úplně libovolný kód, např. na řádku (29) souboru example1.py vidíme testování jména modulu, je-li rovno ‚__main__‘, a modul je tedy načten jako hlavní modul, dojde k vykonání ukázkových příkazů (podobné konstrukce se používají u většiny modulů, viz. zdrojové kódy jednotlivých modulů). Po spuštění těla modulu následuje vytvoření nových jmen v lokálním prostoru jmen bloku, který si vyžádal zavedení modulu.

Řádek (1) ukazuje nejčastější použití konstrukce import, pouze vytvoří v lokálním prostoru jmen proměnnou module1 typu modul. Další řádek (2) ukazuje importování modulu pod jiným jménem. Importovat lze i více modulů najednou, konkrétně to ukazuje řádek (3), přičemž i v tomto případě lze některé moduly importovat pod jiným jménem. Atributem modulu __name__ ale zůstává stále jméno původního modulu. Z modulů můžeme do lokálního prostoru jmen importovat jen některé atributy (4)(5), opět je možné je importovat i pod jinými jmény, než pod jakými jsou obsaženy v modulu. Je-li místo výčtu atributů uvedena hvězdička, zavedou se do lokálního prostoru jmen všechny atributy modulu (s výjimkou jmen, která začínají znakem podtržítko _). Zde je třeba zmínit, že při použití klíčového slova from se nevytvoří proměnná odkazující na modul, ale jen odkazy na ty proměnné, které si programátor přeje naimportovat (a jejichž výčet následuje za slovem import).

Narazí-li interpret na konstrukci import, začne hledat modul, který odpovídá požadovanému jménu. Nejprve se podívá do interního seznamu dosud importovaných modulů, není-li již zaveden. Tento seznam je přístupný jako sys.modules (čili proměnná ‚modules‘ v modulu ‚sys‘). Jde o asociativní pole, jehož klíči jsou jména modulů a hodnotami moduly, na než jména odkazují. Do tohoto pole je umožněn i zápis, čili je možné takto vytvářet i nové moduly, aniž by byly fyzicky přítomny v souborovém systému (doporučuji si prohlédnout zdrojový kód modulu os, který takto vytváří modul os.path).

Není-li modul nalezen v interním seznamu, pokračuje se s jeho hledáním v aktuálním adresáři a pak v adresářích v seznamu sys.path (který se generuje z proměnné prostředí $PYTHONPATH a z cest závislých na instalaci jazyka). Pro modul ‚module1‘ se nejprve hledá soubor ‚module1.pyc‘, což je „zkompilovaná“ verze souboru ‚module1.py‘, není-li nalezen, zavede se soubor module1.py. Je-li soubor module1.py novější než module1.pyc, předpokládá interpetr, že obsah module1.pyc je neaktuální a vytvoří ho znovu z module1.py. Zároveň se vytvoří i soubor ‚module1.pyc‘, který urychlí příští zavedení modulu (není-li ho možné vytvořit, nic se neděje, pouze bude další start zpomalen kompilací). Je dobré vědět, že spuštěním hlavního modulu (přístupného jako ‚__main__‘) nedojde k jeho kompilaci, proto je výhodnější napsat menší spouštěcí skript, jenž zavolá hlavní funkci ve velkém modulu, který se již zkompiluje. Dosáhneme tím rychlejšího spouštění programu. Taktéž je možné používat pouze zkompilované .pyc moduly, ze kterých se nedá zpět získat původní zdrojový kód, Python takto umožňuje vývoj i closed-source programů.

Není-li modul nalezen, dojde k výjimce ImportError, je-li modul nalezen, ale není syntakticky správně, vyvolá interpret výjimku SyntaxError. Vznikne-li výjimka při inicalizaci modulu, rozšíří se do volajícího bloku a modul zůstane NEZINICIALIZOVANÝ! Přesto se vytvoří záznam v sys.modules. Proto si dávejte pozor a důsledně kontrolujte výjimky, které se mohou při importování modulu rozšírit, poněvadž pokusíme-li se importovat nezinicializovaný modul, interpret nám bez problémů vyhoví!

Balíčky

Pro velké kolekce modulů se vyplatí používat více modulů a i zde přichází Python s výraznou pomocí, kterou jsou balíčky. Jedná se vlastně o moduly uložené v adresářové sturktuře. Narazí-li interpret na konstrukci, která požaduje importování modulu s hiearchickým jménem, např: import package.module, považuje ‚package‘ za balíček a ‚module‘ za modul, který je v něm uložený.

Nejjednodušším balíčkem je pouhý adresář umístěný v místech, kde se vyhledávají klasické moduly, a který obsahuje soubor __init__.py a samozřejmě moduly v něm uložené. Soubor __init__.py se spustí jako inicializační část balíčku, poté se přistoupí k importování modulů z balíčku. Soubor __init__.py může teoreticky být i pouhý prázdný soubor, v praxi se tak ovšem (většinou) neděje.

Proměnná __all__

Co se stane, když ale programátor požaduje konstrukcí from package import * naimportování všech modulů z balíčku? Dalo by se očekávat, že se naimportují všechny moduly v balíčku. Na různých platformách ale platí různé konvence pro zápis jmen souborů, konkrétně Windows (o DOS nemluvě) nerozlišují velikost písmen v souborech a soubor FOO.PY by mohl být importován jako modul FOO, ale také jako Foo nebo foo. Proto se v inicializační části modulu nastavuje proměnná __all__, což je seznam řetězců, které explicitně určují, jaké moduly balíček obsahuje. Udržování této proměnné je pouze na autorovi balíčku, který ručí za to, že odpovídá skutečnosti.

Není-li proměnná __all__ nastavena, je možné rovněž vykonat konstrukci from package import *, ale v aktuálním prostoru jmen se vytvoří odkazy jen na proměnné z globálního prostoru jmen souboru __init__.py balíčku ‚package‘. Chce-li programátor z modulu obsaženého v nějakém balíčku importovat jiný modul z téhož balíčku, musí jeho jméno zapsat plnou cestou. Máme-li balíček Languages, který obsahuje moduly Python, Perl a Ruby, a v modulu Python chceme používat modul Ruby, musíme ho importovat jako Languages.Ruby. Je samozřejmé, že balíčky mohou kromě modulů obsahovat další balíčky a lze tak vytvořit velice jemnou strukturu modulů.

root_podpora

Protože modul je stejně jako libovolný objekt pouze odkaz do paměti interpretu, lze ho tímto způsobem používat. Používáme-li v nějakém modulu jiné moduly, je slušností je v době, kdy je již nebudeme potřebovat, odstranit z prostoru jmen konstrukcí del (např. del Languages.Ruby).

Příště

Dnešní pokračování našeho seriálu je u konce. Celý příští díl našeho povídání bude věnován třídám a jejich instancím. Probereme si, jak třídy používat, jak se přistupuje k instanci třídy z její metody a jakým způsobem můžeme dynamicky vytvářet atributy instancí. Pozor, celý příští výklad se již bude týkat nové verze 2.2!

Byl pro vás článek přínosný?