Lokální proměnné

Už umíš definovat vlastní funkce. Zbývá ale ještě dovysvětlit, jak v nich fungují proměnné.

Funkce může používat proměnné „zvnějšku“. Následující program přiřadí do proměnné pi a všechny další funkce mají k pi přístup:

pi = 3.1415926

def obsah_kruhu(polomer):
    return pi * polomer ** 2

print(obsah_kruhu(100))

Jinak je tomu ale v případě, kdy proměnnou nastavíš uvnitř funkce.

Všechny argumenty a všechny proměnné, do kterých funkce přiřazuje, jsou úplně nové proměnné, které nemají nic společného s tím, co je „venku“ kolem funkce.

Těmto proměnným se říká lokální proměnné (angl. local variables), protože existují jen místně, v rámci volání jedné jediné funkce.

Proměnné, které nejsou lokální, jsou globální – ty existují v celém programu.

Pro příklad:

def nastav_x(hodnota):
    x = hodnota  # Přiřazení do lokální proměnné!

nastav_x(40)
print('x =', x)

Program skončí s chybou! Funkce nastav_x si hraje na vlastním písečku; proměnná x je jen pro ni. Když funkce nastav_x skončí, proměnná x přestane existovat.

Přiřazení

To, co dělá lokální proměnnou, je přiřazení. Porovnej nastav_x s příkladem na obsah_kruhu výše: rozdíl mezi pi a x je v tom, že do x funkce přiřazuje.

Co je to přiřazení? Všechno, co nastavuje nějakou proměnnou. Například:

  • Klasika je přiřazovat pomocí =, např. a = 3.
  • Argumenty funkce: funkce def nastav_x(hodnota) přiřadí do hodnota,
  • Cyklus for x in ...: přiřazuje do proměnné x.
  • Pro úplnost, příkazy def x(...):, import x a from ... import x taky přiřazují do x – ale ve funkcích se moc nepoužívají.

A další

K těmto materiálům se možná budeš vracet, tak pro úplnost přidám další způsoby, které postupně poznáš. Není jich mnoho:

  • Příkazy with ... as x, del x, except ... as x přiřazují do x.
  • Přiřazují i speciální přiřazovací operátory jako +=, *=, :=.

Zakrývání jména

Jak to funguje, když ve funkci přiřadíš do proměnné, která existuje i globálně? Pak tu máme problém.

Vytvoří se úplně nová lokální proměnná, která má stejné jméno jako ta globální. Jméno označuje lokální proměnnou, a ta globální pak „není vidět“.

Tento příklad tedy nebude fungovat tak, jak se zdá:

x = 0

def nastav_x(hodnota):
    x = hodnota  # Přiřazení do lokální proměnné!
    print('Ve funkci nastav_x: x =', x)

nastav_x(40)
print('Venku: x =', x)

V tomto programu existují dvě proměnné jménem x. Jedna je globální. Jedna je lokální pro funkci nastav_x. Jmenují se stejně, ale jsou to dvě různé proměnné.

Lokální nebo globální?

Pojďme si to ukázat. Než spustíš tenhle program, zkus předpovědět, co bude dělat. Pak ho pusť, a pokud dělal něco jiného, zkus vysvětlit proč. Pozor na chytáky!

from math import pi
obsah = 0
a = 30

def obsah_elipsy(a, b):
    obsah = pi * a * b  # Přiřazení do `obsah`
    a = a + 3  # Přiřazení do `a`
    return obsah

print(obsah_elipsy(a, 20))
print(obsah)
print(a)

Zkus odpovědět na tyto otázky:

  • Je proměnná pi lokální, nebo globální?
  • Je proměnná obsah lokální, nebo globální?
  • Je proměnná a lokální, nebo globální?
  • Je proměnná b lokální, nebo globální?
  • Je proměnná obsah_elipsy lokální, nebo globální?

Řešení

Rada na závěr

Pravidla pro lokální proměnné jsou pro začátečníky jednou z nejzvláštnějších věcí v Pythonu. Jsou ale užitečná – umožňují některé užitečné techniky, např. rekurzi.

Jestli ti to celé připadá složité, dá se tomu zatím vyhnout dodržováním jednoho pravidla: nepřiřazuj ve funkcích do proměnných, které existují i vně funkce. (Parametr funkce se počítá jako přiřazení.)

{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2019/brno-jaro-2019-pondeli:file:3",
      "title": "Lokální proměnné",
      "html": "\n          \n    \n\n    <h1>Lok&#xE1;ln&#xED; prom&#x11B;nn&#xE9;</h1>\n<p>U&#x17E; um&#xED;&#x161; definovat vlastn&#xED; funkce.\nZb&#xFD;v&#xE1; ale je&#x161;t&#x11B; dovysv&#x11B;tlit, jak v&#xA0;nich funguj&#xED; prom&#x11B;nn&#xE9;.</p>\n<p>Funkce m&#x16F;&#x17E;e pou&#x17E;&#xED;vat prom&#x11B;nn&#xE9; &#x201E;zvn&#x11B;j&#x161;ku&#x201C;.\nN&#xE1;sleduj&#xED;c&#xED; program p&#x159;i&#x159;ad&#xED; do prom&#x11B;nn&#xE9; <code>pi</code> a v&#x161;echny dal&#x161;&#xED; funkce\nmaj&#xED; k&#xA0;<code>pi</code> p&#x159;&#xED;stup:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">pi</span> <span class=\"o\">=</span> <span class=\"mf\">3.1415926</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">obsah_kruhu</span><span class=\"p\">(</span><span class=\"n\">polomer</span><span class=\"p\">):</span>\n    <span class=\"k\">return</span> <span class=\"n\">pi</span> <span class=\"o\">*</span> <span class=\"n\">polomer</span> <span class=\"o\">**</span> <span class=\"mi\">2</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">obsah_kruhu</span><span class=\"p\">(</span><span class=\"mi\">100</span><span class=\"p\">))</span>\n</pre></div><p>Jinak je tomu ale v&#xA0;p&#x159;&#xED;pad&#x11B;, kdy prom&#x11B;nnou nastav&#xED;&#x161; <em>uvnit&#x159;</em> funkce.</p>\n<p>V&#x161;echny argumenty a v&#x161;echny prom&#x11B;nn&#xE9;, do kter&#xFD;ch funkce p&#x159;i&#x159;azuje,\njsou <em>&#xFA;pln&#x11B; nov&#xE9;</em> prom&#x11B;nn&#xE9;, kter&#xE9; nemaj&#xED; nic\nspole&#x10D;n&#xE9;ho s t&#xED;m, co je &#x201E;venku&#x201C; kolem funkce.</p>\n<p>T&#x11B;mto prom&#x11B;nn&#xFD;m se &#x159;&#xED;k&#xE1; <em>lok&#xE1;ln&#xED; prom&#x11B;nn&#xE9;</em> (angl. <em>local variables</em>),\nproto&#x17E;e existuj&#xED; jen m&#xED;stn&#x11B;, v r&#xE1;mci vol&#xE1;n&#xED; jedn&#xE9; jedin&#xE9; funkce.</p>\n<p>Prom&#x11B;nn&#xE9;, kter&#xE9; nejsou lok&#xE1;ln&#xED;, jsou <em>glob&#xE1;ln&#xED;</em> &#x2013; ty existuj&#xED; v cel&#xE9;m programu.</p>\n<p>Pro p&#x159;&#xED;klad:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">nastav_x</span><span class=\"p\">(</span><span class=\"n\">hodnota</span><span class=\"p\">):</span>\n    <span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">hodnota</span>  <span class=\"c1\"># P&#x159;i&#x159;azen&#xED; do lok&#xE1;ln&#xED; prom&#x11B;nn&#xE9;!</span>\n\n<span class=\"n\">nastav_x</span><span class=\"p\">(</span><span class=\"mi\">40</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;x =&apos;</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"p\">)</span>\n</pre></div><p>Program skon&#x10D;&#xED; s chybou!\nFunkce <code>nastav_x</code> si hraje na vlastn&#xED;m p&#xED;se&#x10D;ku; prom&#x11B;nn&#xE1; <code>x</code> je jen\npro ni.\nKdy&#x17E; funkce <code>nastav_x</code> skon&#x10D;&#xED;, prom&#x11B;nn&#xE1; <code>x</code> p&#x159;estane existovat.</p>\n<h2>P&#x159;i&#x159;azen&#xED;</h2>\n<p>To, co d&#x11B;l&#xE1; lok&#xE1;ln&#xED; prom&#x11B;nnou, je <em>p&#x159;i&#x159;azen&#xED;</em>.\nPorovnej <code>nastav_x</code> s&#xA0;p&#x159;&#xED;kladem na <code>obsah_kruhu</code> v&#xFD;&#x161;e: rozd&#xED;l mezi <code>pi</code> a <code>x</code>\nje v tom, &#x17E;e do <code>x</code> funkce p&#x159;i&#x159;azuje.</p>\n<p>Co je to p&#x159;i&#x159;azen&#xED;? V&#x161;echno, co <em>nastavuje</em> n&#x11B;jakou prom&#x11B;nnou. Nap&#x159;&#xED;klad:</p>\n<ul>\n<li>Klasika je p&#x159;i&#x159;azovat pomoc&#xED; <code>=</code>, nap&#x159;. <code>a = 3</code>.</li>\n<li>Argumenty funkce: funkce <code>def nastav_x(hodnota)</code> p&#x159;i&#x159;ad&#xED; do <code>hodnota</code>,</li>\n<li>Cyklus <code>for x in ...:</code> p&#x159;i&#x159;azuje do prom&#x11B;nn&#xE9; <code>x</code>.</li>\n<li>Pro &#xFA;plnost, p&#x159;&#xED;kazy <code>def x(...):</code>, <code>import x</code> a <code>from ... import x</code> taky\np&#x159;i&#x159;azuj&#xED; do <code>x</code> &#x2013; ale ve funkc&#xED;ch se moc nepou&#x17E;&#xED;vaj&#xED;.</li>\n</ul>\n<div class=\"admonition note\"><p class=\"admonition-title\">A dal&#x161;&#xED;</p>\n<p>K&#xA0;t&#x11B;mto materi&#xE1;l&#x16F;m se mo&#x17E;n&#xE1; bude&#x161; vracet, tak pro &#xFA;plnost p&#x159;id&#xE1;m dal&#x161;&#xED;\nzp&#x16F;soby, kter&#xE9; postupn&#x11B; pozn&#xE1;&#x161;. Nen&#xED; jich mnoho:</p>\n<ul>\n<li>P&#x159;&#xED;kazy <code>with ... as x</code>, <code>del x</code>, <code>except ... as x</code> p&#x159;i&#x159;azuj&#xED; do <code>x</code>.</li>\n<li>P&#x159;i&#x159;azuj&#xED; i speci&#xE1;ln&#xED; p&#x159;i&#x159;azovac&#xED; oper&#xE1;tory jako <code>+=</code>, <code>*=</code>, <code>:=</code>.</li>\n</ul>\n</div><h2>Zakr&#xFD;v&#xE1;n&#xED; jm&#xE9;na</h2>\n<p>Jak to funguje, kdy&#x17E; ve funkci p&#x159;i&#x159;ad&#xED;&#x161; do prom&#x11B;nn&#xE9;, kter&#xE1; existuje i glob&#xE1;ln&#x11B;?\nPak tu m&#xE1;me probl&#xE9;m.</p>\n<p>Vytvo&#x159;&#xED; se <em>&#xFA;pln&#x11B; nov&#xE1;</em> lok&#xE1;ln&#xED; prom&#x11B;nn&#xE1;, kter&#xE1; m&#xE1; stejn&#xE9; jm&#xE9;no jako\nta glob&#xE1;ln&#xED;.\nJm&#xE9;no ozna&#x10D;uje lok&#xE1;ln&#xED; prom&#x11B;nnou, a ta glob&#xE1;ln&#xED; pak &#x201E;nen&#xED; vid&#x11B;t&#x201C;.</p>\n<p>Tento p&#x159;&#xED;klad tedy nebude fungovat tak, jak se zd&#xE1;:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">nastav_x</span><span class=\"p\">(</span><span class=\"n\">hodnota</span><span class=\"p\">):</span>\n    <span class=\"n\">x</span> <span class=\"o\">=</span> <span class=\"n\">hodnota</span>  <span class=\"c1\"># P&#x159;i&#x159;azen&#xED; do lok&#xE1;ln&#xED; prom&#x11B;nn&#xE9;!</span>\n    <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Ve funkci nastav_x: x =&apos;</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"p\">)</span>\n\n<span class=\"n\">nastav_x</span><span class=\"p\">(</span><span class=\"mi\">40</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Venku: x =&apos;</span><span class=\"p\">,</span> <span class=\"n\">x</span><span class=\"p\">)</span>\n</pre></div><p>V&#xA0;tomto programu existuj&#xED; <em>dv&#x11B;</em> prom&#x11B;nn&#xE9; jm&#xE9;nem <code>x</code>.\nJedna je glob&#xE1;ln&#xED;. Jedna je lok&#xE1;ln&#xED; pro funkci <code>nastav_x</code>.\nJmenuj&#xED; se stejn&#x11B;, ale jsou to dv&#x11B; r&#x16F;zn&#xE9; prom&#x11B;nn&#xE9;.</p>\n<h2>Lok&#xE1;ln&#xED; nebo glob&#xE1;ln&#xED;?</h2>\n<p>Poj&#x10F;me si to uk&#xE1;zat.\nNe&#x17E; spust&#xED;&#x161; tenhle program, zkus p&#x159;edpov&#x11B;d&#x11B;t, co bude d&#x11B;lat.\nPak ho pus&#x165;, a pokud d&#x11B;lal n&#x11B;co jin&#xE9;ho, zkus vysv&#x11B;tlit pro&#x10D;.\nPozor na chyt&#xE1;ky!</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">math</span> <span class=\"kn\">import</span> <span class=\"n\">pi</span>\n<span class=\"n\">obsah</span> <span class=\"o\">=</span> <span class=\"mi\">0</span>\n<span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"mi\">30</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n    <span class=\"n\">obsah</span> <span class=\"o\">=</span> <span class=\"n\">pi</span> <span class=\"o\">*</span> <span class=\"n\">a</span> <span class=\"o\">*</span> <span class=\"n\">b</span>  <span class=\"c1\"># P&#x159;i&#x159;azen&#xED; do `obsah`</span>\n    <span class=\"n\">a</span> <span class=\"o\">=</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"mi\">3</span>  <span class=\"c1\"># P&#x159;i&#x159;azen&#xED; do `a`</span>\n    <span class=\"k\">return</span> <span class=\"n\">obsah</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">obsah_elipsy</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"mi\">20</span><span class=\"p\">))</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">obsah</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">)</span>\n</pre></div><p>Zkus odpov&#x11B;d&#x11B;t na tyto ot&#xE1;zky:</p>\n<ul>\n<li>Je prom&#x11B;nn&#xE1; <code>pi</code> lok&#xE1;ln&#xED;, nebo glob&#xE1;ln&#xED;?</li>\n<li>Je prom&#x11B;nn&#xE1; <code>obsah</code> lok&#xE1;ln&#xED;, nebo glob&#xE1;ln&#xED;?</li>\n<li>Je prom&#x11B;nn&#xE1; <code>a</code> lok&#xE1;ln&#xED;, nebo glob&#xE1;ln&#xED;?</li>\n<li>Je prom&#x11B;nn&#xE1; <code>b</code> lok&#xE1;ln&#xED;, nebo glob&#xE1;ln&#xED;?</li>\n<li>Je prom&#x11B;nn&#xE1; <code>obsah_elipsy</code> lok&#xE1;ln&#xED;, nebo glob&#xE1;ln&#xED;?</li>\n</ul>\n<div class=\"solution\" id=\"solution-0\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/brno-jaro-2019-pondeli/beginners/local-variables/index/solutions/0/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <ul>\n<li><code>pi</code> je glob&#xE1;ln&#xED; &#x2013; nep&#x159;i&#x159;azuje se do n&#xED; ve funkci;\nje &#x201E;vid&#x11B;t&#x201C; v&#xA0;cel&#xE9;m programu.</li>\n<li>Prom&#x11B;nn&#xE9; <code>obsah</code> jsou v programu dv&#x11B; &#x2013; jedna glob&#xE1;ln&#xED;,\na jedna je lok&#xE1;ln&#xED; pro funkci <code>obsah_elipsy</code>,\nproto&#x17E;e do n&#xED; tahle funkce p&#x159;i&#x159;azuje.</li>\n<li>Prom&#x11B;nn&#xE9; <code>a</code> jsou taky dv&#x11B;, podobn&#x11B; jako <code>obsah</code>.\nTady byl chyt&#xE1;k: p&#x159;&#xED;kaz <code>a = a + 3</code> nem&#xE1; &#x17E;&#xE1;dn&#xFD; smysl;\ndo <code>a</code> se sice ulo&#x17E;&#xED; v&#x11B;t&#x161;&#xED; &#x10D;&#xED;slo, ale vz&#xE1;p&#x11B;t&#xED; funkce <code>obsah_elipsy</code> skon&#x10D;&#xED;\na jej&#xED; lok&#xE1;ln&#xED; prom&#x11B;nn&#xE1; <code>a</code> p&#x159;estane existovat.</li>\n<li>Prom&#x11B;nn&#xE1; <code>b</code> je jenom lok&#xE1;ln&#xED; &#x2013; jako argument funkce <code>obsah_elipsy</code>.</li>\n<li>Prom&#x11B;nn&#xE1; <code>obsah_elipsy</code> je glob&#xE1;ln&#xED; (a je v n&#xED; funkce).</li>\n</ul>\n<div class=\"admonition note\"><p class=\"admonition-title\">A pro &#xFA;plnost</p>\n<ul>\n<li>Kl&#xED;&#x10D;ov&#xE1; slova <code>from</code>, <code>import</code>, <code>def</code>, <code>return</code> nejsou prom&#x11B;nn&#xE9;.</li>\n<li>Jm&#xE9;no modulu <code>math</code> nen&#xED; prom&#x11B;nn&#xE1;.</li>\n<li>Prom&#x11B;nn&#xE1; <code>print</code> se d&#xE1; pova&#x17E;ovat za glob&#xE1;ln&#xED;.\n(Ve skute&#x10D;nosti existuje zvl&#xE1;&#x161;tn&#xED; kategorie <em>zabudovan&#xFD;ch</em> (angl. <em>builtin</em>)\nprom&#x11B;nn&#xFD;ch.)</li>\n</ul>\n</div>\n    </div>\n</div><h2>Rada na z&#xE1;v&#x11B;r</h2>\n<p>Pravidla pro lok&#xE1;ln&#xED; prom&#x11B;nn&#xE9; jsou pro za&#x10D;&#xE1;te&#x10D;n&#xED;ky jednou z&#xA0;nejzvl&#xE1;&#x161;tn&#x11B;j&#x161;&#xED;ch \nv&#x11B;c&#xED; v&#xA0;Pythonu.\nJsou ale u&#x17E;ite&#x10D;n&#xE1; &#x2013; umo&#x17E;&#x148;uj&#xED; n&#x11B;kter&#xE9; u&#x17E;ite&#x10D;n&#xE9; techniky, nap&#x159;. rekurzi.</p>\n<p>Jestli ti to cel&#xE9; p&#x159;ipad&#xE1; slo&#x17E;it&#xE9;, d&#xE1; se tomu zat&#xED;m vyhnout dodr&#x17E;ov&#xE1;n&#xED;m jednoho\npravidla:\n<em>nep&#x159;i&#x159;azuj ve funkc&#xED;ch do prom&#x11B;nn&#xFD;ch, kter&#xE9; existuj&#xED; i vn&#x11B; funkce.</em>\n(Parametr funkce se po&#x10D;&#xED;t&#xE1; jako p&#x159;i&#x159;azen&#xED;.)</p>\n\n\n        "
    }
  }
}