V domácích projektech budeš rozdělovat 1D Piškvorky na několik modulů. Výsledek bude vypadat třeba nějak takhle: (Šipky mezi moduly znázorňují importování.)
┌──────────────────╮ ┌───────────────╮ ┌──────────────────╮
│ ai.py │ │ piskvorky.py │ │ hra.py │
├──────────────────┤ ├───────────────┤ ├──────────────────┤
│ │◀-│ import ai │◀-│ import piskvorky │
├──────────────────┤ ├───────────────┤ ├──────────────────┤
│ def tah_pocitace │ │ def vyhodnot │ │ │
│ │ │ def tah │ │ │
└──────────────────┘ │ def tah_hrace │ └──────────────────┘
│ │
└───────────────┘
▲
│
│ ┌───────────────────╮
│ │ test_piskvorky.py │
│ ├───────────────────┤
└─│ import piskvorky │
├───────────────────┤
│ def test_... │
│ │
└───────────────────┘
Jenže funkce tah_pocitace
většinou potřebuje volat funkci tah
.
Co s tím?
Můžeš importovat ai
z piskvorky
a zároveň
piskvorky
z ai
?
┌──────────────────╮ ┌───────────────╮
│ ai.py │ │ piskvorky.py │
├──────────────────┤ ├───────────────┤
│ │◀-│ import ai │
│ import piskvorky │-▶│ │
│ │ │ │
│ def tah_pocitace │ │ def vyhodnot │
│ │ │ def tah │
└──────────────────┘ │ def tah_hrace │
│ │
└───────────────┘
Můžeme se na to podívat z hlediska Pythonu,
který příkazy v souborech vykonává.
Když má importovat soubor piskvorky.py
, začne ho
zpracovávat řádek po řádku,
když tu (docela brzo) narazí na příkaz import ai
.
Otevře tedy soubor ai.py
a začne ho zpracovávat řádek po řádku.
Brzy narazí na příkaz import piskvorky
. Co teď?
Aby nenastala situace podobná nekonečné smyčce –
jeden soubor by importoval druhý, druhý zase první,
a tak stále dokola –
udělá Python taková malý „podvod“:
když zjistí, že soubor piskvorky.py
už importuje, zpřístupní v modulu ai
modul piskvorky
tak, jak ho
má: nekompletní, bez většiny funkcí co v něm mají
být nadefinované.
A až potom, co dokončí import ai.py
,
se vrátí k souboru piskvorky.py
a pokračuje v provádění příkazů def
které v něm jsou.
Takový nekompletní modul může být občas užitečný,
ale ve většině případů se chová skoro
nepředvídatelně a tudíž nebezpečně.
Jinými slovy: když se dva moduly importují navzájem, nemusí to fungovat podle očekávání.
Téhle situaci se budeme chtít vyvarovat.
Jak na to? Máme dvě možnosti.
První možnost je importovat funkci tah
v modulu ai
a používat ji odtamtud.
To je jednoduché, ale nerespektuje účel modulu
ai
, který má obsahovat jenom logiku
vybírání tahu počítače, a ne pomocné funkce, které
můžou být potřeba i jinde.
┌──────────────────╮ ┌───────────────╮
│ ai.py │ │ piskvorky.py │
├──────────────────┤ ├───────────────┤
│ │◀-│ import ai │
│ │ │ │
│ def tah_pocitace │ │ def vyhodnot │
│ def tah │ │ def tah_hrace │
│ │ │ │
└──────────────────┘ └───────────────┘
Druhá možnost je definovat nový, sdílený modul,
který se použije jak v piskvorky.py
tak v ai.py
.
Takový modul se často se pojmenovává
util.py
(z angl. utility, pomůcka, nástroj).
┌──────────────────╮
│ util.py │
├──────────────────┤
│ def tah │
└──────────────────┘
▲ ▲
│ │
┌──────────────────╮ │ │ ┌───────────────╮
│ ai.py │ │ │ │ piskvorky.py │
├──────────────────┤ │ │ ├───────────────┤
│ import util │──┘ └──│ import util │
│ │◀───────│ import ai │
│ │ │ │
│ def tah_pocitace │ │ def vyhodnot │
│ │ │ def tah_hrace │
│ │ │ │
└──────────────────┘ └───────────────┘
Nevýhoda pomocného modulu je ta, že se z něj může stát neudržované „odkladiště“ všeho kódu, který byl jednou potřeba na dvou nebo více místech.
Pro kterou z možností se rozhodnout, záleží na situaci. Programování není vždycky jen exaktní věda!
{ "data": { "sessionMaterial": { "id": "session-material:2019/brno-jaro-2019-ut:tests:1", "title": "Cyklické importy", "html": "\n \n \n\n <h2>Cyklické importy</h2>\n<p>V domácích projektech budeš rozdělovat 1D Piškvorky na několik modulů.\nVýsledek bude vypadat třeba nějak takhle:\n(Šipky mezi moduly znázorňují importování.)</p>\n<div class=\"highlight\"><pre><code>┌──────────────────╮ ┌───────────────╮ ┌──────────────────╮ \n│ ai.py │ │ piskvorky.py │ │ hra.py │\n├──────────────────┤ ├───────────────┤ ├──────────────────┤\n│ │◀-│ import ai │◀-│ import piskvorky │\n├──────────────────┤ ├───────────────┤ ├──────────────────┤\n│ def tah_pocitace │ │ def vyhodnot │ │ │\n│ │ │ def tah │ │ │\n└──────────────────┘ │ def tah_hrace │ └──────────────────┘\n │ │\n └───────────────┘\n ▲\n │\n │ ┌───────────────────╮\n │ │ test_piskvorky.py │\n │ ├───────────────────┤\n └─│ import piskvorky │\n ├───────────────────┤\n │ def test_... │\n │ │\n └───────────────────┘</code></pre></div><p>Jenže funkce <code>tah_pocitace</code>\nvětšinou potřebuje volat funkci <code>tah</code>.\nCo s tím?\nMůžeš importovat <code>ai</code> z <code>piskvorky</code> a zároveň\n<code>piskvorky</code> z <code>ai</code>?</p>\n<div class=\"highlight\"><pre><code>┌──────────────────╮ ┌───────────────╮\n│ ai.py │ │ piskvorky.py │\n├──────────────────┤ ├───────────────┤\n│ │◀-│ import ai │\n│ import piskvorky │-▶│ │\n│ │ │ │\n│ def tah_pocitace │ │ def vyhodnot │\n│ │ │ def tah │\n└──────────────────┘ │ def tah_hrace │\n │ │\n └───────────────┘</code></pre></div><p>Můžeme se na to podívat z hlediska Pythonu,\nkterý příkazy v souborech vykonává.\nKdyž má importovat soubor <code>piskvorky.py</code>, začne ho\nzpracovávat řádek po řádku,\nkdyž tu (docela brzo) narazí na příkaz <code>import ai</code>.\nOtevře tedy soubor <code>ai.py</code>\na začne ho zpracovávat řádek po řádku.\nBrzy narazí na příkaz <code>import piskvorky</code>. Co teď?</p>\n<p>Aby nenastala situace podobná nekonečné smyčce –\njeden soubor by importoval druhý, druhý zase první,\na tak stále dokola –\nudělá Python taková malý „podvod“:\nkdyž zjistí, že soubor <code>piskvorky.py</code>\nuž importuje, zpřístupní v modulu <code>ai</code>\nmodul <code>piskvorky</code> tak, jak ho\nmá: nekompletní, bez většiny funkcí co v něm mají\nbýt nadefinované.\nA až potom, co dokončí import <code>ai.py</code>,\nse vrátí k souboru <code>piskvorky.py</code>\na pokračuje v provádění příkazů <code>def</code> které v něm jsou.\nTakový nekompletní modul může být občas užitečný,\nale ve většině případů se chová skoro\nnepředvídatelně a tudíž nebezpečně.</p>\n<p>Jinými slovy: když se dva moduly importují navzájem,\nnemusí to fungovat podle očekávání.</p>\n<p>Téhle situaci se budeme chtít vyvarovat.</p>\n<p>Jak na to? Máme dvě možnosti.</p>\n<h2>Organizace modulů podle závislostí</h2>\n<p>První možnost je importovat funkci <code>tah</code> v modulu <code>ai</code>\na používat ji odtamtud.\nTo je jednoduché, ale nerespektuje účel modulu\n<code>ai</code>, který má obsahovat jenom logiku\nvybírání tahu počítače, a ne pomocné funkce, které\nmůžou být potřeba i jinde.</p>\n<div class=\"highlight\"><pre><code>┌──────────────────╮ ┌───────────────╮\n│ ai.py │ │ piskvorky.py │\n├──────────────────┤ ├───────────────┤\n│ │◀-│ import ai │\n│ │ │ │\n│ def tah_pocitace │ │ def vyhodnot │\n│ def tah │ │ def tah_hrace │\n│ │ │ │\n└──────────────────┘ └───────────────┘</code></pre></div><h2>Pomocný modul</h2>\n<p>Druhá možnost je definovat nový, sdílený modul,\nkterý se použije jak v <code>piskvorky.py</code> tak v <code>ai.py</code>.</p>\n<p>Takový modul se často se pojmenovává\n<code>util.py</code> (z angl. <em>utility</em>, pomůcka, nástroj).</p>\n<div class=\"highlight\"><pre><code> ┌──────────────────╮\n │ util.py │\n ├──────────────────┤\n │ def tah │\n └──────────────────┘\n ▲ ▲\n │ │\n┌──────────────────╮ │ │ ┌───────────────╮\n│ ai.py │ │ │ │ piskvorky.py │\n├──────────────────┤ │ │ ├───────────────┤\n│ import util │──┘ └──│ import util │\n│ │◀───────│ import ai │\n│ │ │ │\n│ def tah_pocitace │ │ def vyhodnot │\n│ │ │ def tah_hrace │\n│ │ │ │\n└──────────────────┘ └───────────────┘</code></pre></div><p>Nevýhoda pomocného modulu je ta,\nže se z něj může stát neudržované „odkladiště“\nvšeho kódu, který byl jednou potřeba na dvou\nnebo více místech.</p>\n<p>Pro kterou z možností se rozhodnout, záleží\nna situaci.\nProgramování není vždycky jen exaktní věda!</p>\n\n\n " } } }