[python] globální proměnné

p.kosina gen2n na seznam.cz
Úterý Leden 4 15:57:52 CET 2005


Ahoj,

Zvykl jsem si na Outlookový styl, snad mi to projde, že odpovídám nahoře
a ne v textu.

Jsem skutečně spíš praktik, teorii začínám vyhledávat, až když mi něco
nefunguje, takže často asi nepoužívám správné termíny, ale postupně to
vylepšuji :-).

Jaké výhody získám zavedením mé superglobální proměnné oproti obyčejným
globálním proměnným? (ovšem za předpokladu, že je ve svých funkcích
nepřepíši, jak jsi ukazoval (promin ze jsem svou odpoved shrnul do tak
krátké věty) - to samozřejme nesmím, ale na to musím dávat pozor i u
ohromných tříd, abych dvakrát nepoužil self.pocet=...)

1/ nemusím ve všech svých funkcích typovat global x
Ovšem ono to v praxi není takto přehledné global x, ale většinou
global pocetStrel, souradniceX, souradniceY, PocetZivotů,...... a ve 3-5 
funkcích. Prostě program získává na přehlednosti.

2/ bod číslo 2 není....

3/ když potřebuji uvnitř mé funkce proměnnou, ve které
potřebuji, aby zůstala její hodnota i do dalšího volání funkce.
A zase, teoreticky není problém udělat return x, ale v praxi, je tam
těch dlouhojmenných proměnných habakuk.

4/ Tenhle důvod jsem vykoukal v LiveWires: tato superglobalita (proménně
instance třídy) slouží jako jakýsi Kontejner vlastností, které mají
společný základ. Třebas :
hrac=G()
hrax.souradniceX=0
hrac.souradniceY=0
hrac.tvar=...
... Všechno pěkně seskupené pod přiléhavým názvem a k tomi bonus zdarma,
že uvnitř všech funkcí, které nějak ovlivňují hrače nemusím psát "global"

Singleton - je to hezká idea, ale myslím že to není to o co se snažím.

Jinak díky za ty příklady, upopzornili mě na možná úskalí.

Abych i prakticky podpořil mé snažení, mám dva příklady. Jeden se
používá v těch LiveWires a ten druhý tu chci ukázat. Měl jsem včera
potřebu z registru win98 odstranit všechny klíče, kde je 'avg6' (nějak
mi nešla instalace avg7. Nakonec z toho vzniklo rekurzivní volání s
významným zasoupením superglobalnosti, kdy zbytečně funkci stále
nepředávám v agrumentech, co je neměnné (hledaný text, ..) a jako bonus 
jsem získal něco jako g.pocet, kde nasčítám zcela transparentně počet 
výskytů hledaného textu a taky třeba g.cesta, kde appenduji jednotlive 
klice, jak jimi prochazim. Možná ten příklad z LiveWires je hezčí, ale 
tenhle je užitečnější...
SKRIPT NIC NEMAŽE ANI NEMĚNÍ, JEN TISKNE, PŘESTO JE POUŽITÍ JEN NA
VLASTNÍ NEBEZPEČÍ.
http://www.alpha-site.wz.cz/zaci/amos/registrWindows.py


Ufff, 0,5 hodiny psaní + 5 hodin ladění skriptu.
Is it worth it? I hope so.

Pavel




Petr Prikryl napsal(a):
> Ahoj Pavle, (doufám, že tykání nevadí ;)
> 
> 
>>Děkuji za vyčerpávající vysvětlení. Nechci vypadat jako
>>Tlučhuba :-), přesto zkusím ještě jednou můj postoj.
> 
> 
> Já zase nechci vypadat jako přechytralý, ale můj obor
> vzdělání a praxe mi alespoň statisticky zaručuje, že bych se
> v tomto případě nemusel mýlit.
> 
> Pokud jde o postoj, jeho změna by ti možná pomohla posunout
> se o kousek dál. Třeba ti v tom pomůžu.
> 
> 
>>class G:
>>   pass
>>
>>g=G()
>>g.x=30
>>
>>Možná nepoužívám nejsprávnější terminologii, když nazývám x
>>globální proměnnou (ano, je to lokální uvnitř G), nicméně
>>objekt 'g.x' je "veřejně přístupný", *ze všech funkcí
>>měnitelný bez jakýchkoliv dalších fíglu v rámci celého
>>programu*, jedná se tedy, podle mne, o mnou obhajovanou
>>superglobalni (superglobalni z hlediska celého programu a ne
>>jednotlivé třídy či instance - z jejich hlediska to je
>>samozřejmě lokální) proměnnou. Ale i výraz veřejně přístupný
>>je OK, nic to na věci nemění. Možná se pletu, možná existují
>>skulinky, kdy to neplatí, nicméně v programu s pár funkcemi
>>a jednou main to funguje.
> 
> 
> Při debatách o programování je opravdu dobré používat
> jednotnou terminologii, protože jinak si všichni můžeme
> věcně myslet to samé a přitom se budeme hádat. "Globálně
> přístupná" znamená přístupná ze všech míst programu bez
> výjimky. Složka x není přístupná odkudkoliv -- pouze přes g.
> Nemůže tedy být globální. Ale protože g je přístupné, pak je
> i jeho veřejná složka přístupná. To nemá s globálností
> přístupu nic společného. Píšeš "(ano, je to lokální uvnitř
> G)". To není pravda. Složka x je přístupná přes g, nikoliv
> přes G. Není součástí třídy, je součástí instance třídy
> (objektu). Třída pouze předepisuje chování, objekt je
> realizuje.
> 
> Zavedením objektu g nic superglobálního nevyřeším. Je to jen
> falešný dojem. Pouze se ti složitějším způsobem podařilo
> vyjádřit něco podobného, jako kdybys vytvořil skutečně
> globální proměnnou x. Funguje to úplně stejně, jenže kvůli
> zvýšení složitosti vyjádření jsi přehlédl, že je to úplně
> stejné. Zkus tohle (pro zjednodušení vynechávám češtinu,
> okopíruj do souboru a spusť):
> 
> ========================================================
> 
> class G:
>    pass
> 
>    
> def fa():
>     g = 'funkce fa() lokalne predefinovala jmeno g'
>     print 'Uvnitr fa():', g
>     
>     try:
>         print 'Pokus o pristup k g.x uvnitr fa():', g.x
>     except:
>         print 'Chyba!'
>         
> 
> def fb():
>     g = G()       # nova, lokalni instance tridy G
>     g.x = 'hodnota lokalni instance objektu -- fb()'
>     print 'Uvnitr fb():', g.x
>     
>     
> g = G()
> g.x = 'superglobalni hodnota'
> 
> print '\n\n\n' + '-' * 20           # jen oddelovac
> print 'Na globalni urovni:', g.x
> 
> fa()
> 
> print 'Na globalni urovni:', g.x
> 
> fb()
> 
> print 'Na globalni urovni:', g.x
> 
> g2 = g        # navazu objekt na jine jmeno
> g = 'nove g'
> 
> print 'Objekt s jinym jmenem:', g2.x    # OK
> print 'Nova hodnota pojmenovana g:', g
> print 'Na globalni urovni:', g.x        # chyba
> 
> ========================================================
> 
> Všimni si, že funkce fa() i fb() definují svou lokální
> proměnnou g a tím (dočasně, v jejich těle) znepřístupní 
> g definovanou na globální úrovni. Funkce fa() mění typ
> proměnné zpřístupňované přes jméno g, funkce fb() zachovává
> dokonce stejný typ (stejnou třídu) objektu. Ale pro celý
> kód uvnitř fb() a pro všechny případné funkce volané z fb()
> znemožňuje přístup k objektu g na globální úrovni. Místo
> něj podstrčí lokálnější g, definovaný na úrovni těla fb().
> 
> Teď přepíšu totéž bez třídy G a jejích instancí. Místo g.x
> budu psát x a místo g2.x budu psát x2. Uvnitř fa() nebude
> nutné ošetřovat přístup ke složce x a zviditelní se tím
> lokálnost nového x. Funkce fb() nyní demonstruje použití
> klíčového slova global, které potlačí vytvoření lokálního x
> (narozdíl od fa()).
> 
> ========================================================
> 
> def fa():
>     x = 'funkce fa() lokalne predefinovala jmeno x'
>     print 'Uvnitr fa():', x
>     
> def fb():
>     global x
>     x = 'nova globalni hodnota x'
>     print 'Uvnitr fb():', x
>     
> x = 'globalni hodnota'
> 
> print '\n\n\n' + '-' * 20           # jen oddelovac
> print 'Na globalni urovni:', x
> 
> fa()
> 
> print 'Na globalni urovni:', x
> 
> fb()
> 
> print 'Na globalni urovni:', x
> 
> x2 = x        # navazu objekt na jine jmeno
> x = 'nove x'
> 
> print 'Objekt s jinym jmenem:', x2    # OK
> print 'Nova hodnota x:', x
> 
> ========================================================
> 
> 
> 
>>Uznávám, není to čisté řešení, přesto mi to připadá
>>"čistější" než použití global. 
> 
> 
> Klíčové slovo 'global' není nečisté. Pouze umožňuje "nečisté
> praktiky" používání globálních proměnných. Tím, že sis
> "nějak" vynutil zabalení x do nadřízené struktury, ses
> jakoby vyhnul použití globální proměnné z hlediska jména x,
> ale nevyhnul ses používání globální proměnné z hlediska
> jména g, jehož je x složkou. Pokud tvoje funkce nebudou
> definovat novou hodnotu g jako celku (neprovedou novou vazbu
> mezi jménem g a jiným objektem) a budou pouze pracovat s
> jeho složkami, pak jsi vyřešil svůj problém.
> 
> Napadá mě, že možná intuitivně směřuješ k řešení, kterému se
> v objektově orientovaném návrhu říká singleton (jeden z
> návrhových vzorů). Singleton je objekt, u kterého je
> zajištěno, že existuje v jediné instanci. To znamená, že i
> když nemusí mít přidělené jméno, vždy se nějak dostanu k
> jedinému objektu. Pokud si nějak zajistím přístup k objektu
> této třídy, pak je zajištěno, že budu pracovat s jediným
> možným a tím samým objektem. Typicky se k němu
> nedostávám přes jméno, ale pokusím se o vytvoření instance
> vyhrazené třídy. Třída ale zajistí, že se objekt vytvoří jen
> poprvé a při ostatních pokusech se místo nových instancí
> vracejí reference na již existující, jediný objekt. Jde 
> návrhový vzor, který se používá napříč různými OO jazyky,
> ale v různých jazycích se implementuje různým způsobem.
> 
> 
>>Když ukazujete "zjednodušování" kódu vypouštěním funkcí f()
>>i r(), tak to nevyjadřuje praxi. V praxi samozřejmě funkce
>>takto jednoduché nejsou a nedají se vypustit, protože "něco
>>dělají" a veřejně přístupné proměnné jsou tam jenom pro
>>usnadnění práce (funkce tam není samoúčelně, kvůli nim), pro
>>zpřehlednění kódu, pro přiblížení se práce v OOP. Snad
>>klasickým případem je vznik nových proměnných ve funkci,
>>které však je třeba si uchovat do dalšího průchodu
>>funkcí,kde se s nimi bude opět pracovat.
> 
> 
> V příkladech z minulé diskuse jsem zaváděl a rušil funkce
> jen proto, abych ukázal, že se zavedením nebo rozepsáním
> funkce principiálně nic nemění. Chtěl jsem tím ukázat, že
> definice funkce do vyjasňování nic nového nevnáší. Chtěl
> jsem také ukázat, že definice třídy G do problému
> globální/lokální proměnná nic nového nevnáší. 
> 
> Funkce se teoreticky dají vypustit vždy -- jejich volání lze
> rozepsat jejich těly --, což ale neznamená, že je jejich
> používání zbytečné. Zde spolu nejsme "ve_při" ;)
> 
> Používání funkcí v Pythonu se od používání funkcí v
> typických kompilovaných jazycích liší v tom, že ačkoliv
> parametry předáváme vždy odkazy (nikdy hodnotami), některé z
> předaných objektů nemůžeme měnit (jsou "immutable", neměnné
> -- typicky čísla a řetězce). Proto, pokud má pythonovská
> funkce modifikovat například nějakou číselnou proměnnou a
> nechceme používat globální proměnnou, musíme používat obrat
> podobný následujícímu:
> 
> def f(x):
>     return x + 1
> 
> v = 10
> for i in xrange(10):
>     v = f(v)
>     print v
>     
> Pro stejný účel nelze použít funkci, která by nevyužívala
> možnost vracet výslednou hodnotu, aniž bychom využili nějaké
> globálně přístupné struktury:
> 
> 
> def f(x):
>     x = x + 1
> 
> v = 10
> for i in xrange(10):
>     f(v)
>     print v
>     
> 
> Naposledy uvedený příklad by fungoval například v Pascalu při
> předávání parametru x odkazem. V Pythonu nebude fungovat
> nikdy, protože příkaz x = x + 1 vede k vytvoření nového
> objektu a k vazbě lokálního jména x na tento nově vytvořený
> objekt. Původní vazba jména v na objekt s hodnotou 10
> zůstává nezměněna. Nově vytvořená vazba x se s ukončením
> těla funkce ztrácí.
>     
> 
>>Já si tedy OOP dost zjednodušuji, třebas když se vytváří
>>nějaký textový editor v OOP, tak vlastně celý program je
>>jedna velká třída a uvnitř ní jsou její proměnné 'self.x'
>>viditelné ze všech jejích metod. Tomu jsem se právě chtěl
>>svým přístupem přiblížit.
> 
> 
> Používání objektově orientovaného přístupu by nemělo být
> cílem, ale prostředkem. Nemá smysl znásilňovat třídy. Pro
> začátek je možná lepší osvojit si práci s moduly a uvnitř
> definovanými funkcemi. Pokud ti práce s třídami a s objekty
> připadá trochu nepřirozená, je dobré zajímat se o to proč.
> 
> Postoj se dá změnit, věci se dají doučit a pochopit.
> Budováním programu jako jedné velké třídy můžeš oddálit svůj
> zážitek, pocit, pochopení významu OOP. Spočívá mimo jiné ve
> změně přístupu k programování. Ta změna nespočívá v tom, že
> přestanu používat "obyčejné" proměnné, "obyčejné" funkce a
> "obyčejné" moduly. Ta změna přístupu spočívá v tom, že začnu
> složitější aplikace řešit lepším způsobem.
> 
> Petr
> 



Další informace o konferenci Python