O imenskih prostorih (namespace) smo se pogovarjali že pri Programiranju 1. V resnici smo se jih bolj kot ne le dotaknili. Kdor je te stvari že pozabil, naj pogleda zapiske na to temo pri Programiranju 1. Takrat smo risali škatlice, ki so predstavljali objekte, in do vsakega objekta, je vodila ena ali več puščic. Na drugi strani puščic so bila imena: vsako ime se je torej nanašalo na nek objekt. Do objektov, na katere se ni neposredno nanašalo nobeno ime, smo lahko prišli posredno: do objektov, shranjenih v seznamu, lahko pridemo z imenom seznama in indeksom; do atributov objektov pridemo z imenom objekta, piko in imenom atributa...

Povedali smo, da ima vsaka funkcija svoj "nabor" imen, prav tako imajo svoje nabore imen moduli in, na nek drug način, tudi razredi in slovarji. Tu bomo začeli danes.

Še prej pa moramo eno stvar povedati bolj formalno.

Vezava imen

Kot stalno ponavljamo: imamo imena in objekte. Imena so vezana (bound) na objekte.

Odkod se vzamejo imena? Ali, če hočete, "spremenljivke"? Odkod se lahko pojavijo? Formalneje, kako lahko privežemo (bind) ime na objekt.

  • S prirejanjem: x = 42 naredi objekt 42 in veže ime x nanj. Kadar v prirejanju razpakiramo terko, npr. a, b = 3, 6, vežemo dve imeni.
  • Z argumenti funkcij: če funkcijo definiramo z def f(a, b), bosta v funkciji imeni a in b, ki bosta vezani na argumente, ki smo jih dali funkciji.
  • Z zanko for: for i in range(5) veže i na vrednosti, ki jih generira range. V vsaki ponovitvi zanke je i vezan na drugo mesto.
  • Uvažanje modulov: import math veže ime math na modul, import math as matematika veže ime matematika na modul, ki mu sicer pravimo math, in from math import sin, cos veže imeni sin in cos na funkciji sin in cos iz modula math.
  • Definicije funkcij in razredov vežejo njihova imena na te funckije in razrede - def f(x): return 42 veže ime f na to funkcijo, class A: pass pa ime A na ta (ničvredni) razred.
  • Operator as v kontekstih (with) in prestrezanjih izjem (except). O slednjem nekaj malega vemo, o drugem pa se bomo tule definitivno pogovarjali, vendar ne danes.

To je vse: imena se vežejo na teh šest načinov in nobenega drugega.

Tema, o kateri se bomo pogovarjali danes, pa so imenski prostori, namespace, "zbirke imen". Teh zbirk je več.

Da kar takoj začnem s spoilerjem: v bistvu so imena shranjena v slovarjih. Včasih zares, včasih pa se vsaj delajo, da so. Ključi tega slovarja so imena, vrednosti pa objekti, na katere se ta imena nanašajo.

Globalni imenski prostor

To je najlažje videti v globalnem imenskem prostoru. Globalne spremenljivke (torej tiste, ki niso bile vezane znotraj funkcije ali razreda), so shranjene v slovarju, ki ga Python pokaže prek funkcije globals().

>>> globals()
{'__name__': '__main__'}
>>> x = 12
>>> globals()
{'__name__': '__main__', 'x': 12}
>>> ime = "Benjamin"
>>> globals()
{'ime': 'Benjamin', 'x': 12, '__name__': '__main__'}

globals() nam torej vrne slovar z imeni in pripadajočimi vrednostmi. Ta že v začetku ni prazen. V njem se nahaja, recimo, čudno ime __name__ z vrednostjo "__main__"; kaj je to in čemu služi, bomo še videli, ni pa posebej pomembno. Še manj zanimive so nekatere druge stvari, ki sem jih zaradi preglednosti kar pobrisal.

Ko napišemo x = 12, se v slovarju pojavi nov element; ključ je niz "x", vrednost pa 12. Podobno se zgodi, ko napišemo ime = "Benjamin".

Zdaj pa vtipkajmo x + 7. Python najprej preveri, ali obstaja ime x: pogleda v tale slovar. Če obstaja ključ x, v izrazu x + 7 uporabi pripadajočo vrednost (globals()["x"]). Če ga ni, javi napako.

Napišem lahko tudi del x, kar pobriše ime x - torej, pobriše dotični ključ (in, seveda, pripadajočo vrednost) iz slovarja.

 >>> del x
 >>> globals()
 {'ime': 'Benjamin', '__name__': '__main__'}

Da je to v resnici slovar, ki veže imena na vrednosti, se prepričamo tako, da ga uporabljamo za "ročno" vezanje imen.

>>> imena = globals()
>>> imena
{'ime': 'Benjamin', 'imena': {...}, '__name__': '__main__'}

Zdaj se ime imena nanaša na slovar, ki ga vrne globals(). Ta slovar je slovar imen in zato, he he, vsebuje tudi ključ "imena", pripadajoča vrednost pa je kar ta slovar sam (in vsebuje ključ "imena" in tako naprej).

No, nisem hotel pokazati tega. :) Zanimivo je tole: v slovar imena dodajmo kak nov element:

>>> imena["a"] = 42
>>> imena["s"] = [1, 2, 3]
>>> imena
{'a': 42, 'ime': 'Benjamin', 's': [1, 2, 3], 'imena': {...},
'__name__': '__main__'}

S tem sta a in s v resnici dve novi spremenljivki:

>>> a
42
>>> s
[1, 2, 3]

Če torej napišemo a = 42 (kot bi počeli normalno) je to samo bližnjica za globals()["a"] = 42 (če smo natančni, bližnjica za globals().__setitem__("a", 42), ampak tako daleč v drobovje Pythona danes še ne bomo rinili).

Podobno je x + 7 samo bližnjica za globals()["x"] + 7. V resnici se prvo "prevede" v drugo (ali, če smo natančni, v nekaj podobnega drugemu).

Kar smo pravkar videli, ni posebej uporabno. Tu je, ker pač Python tega ne skriva. Ni pa mišljeno, da bi se to uporabljalo. Tudi mi smo pogledali samo zato, da smo videli, kako Python v resnici deluje. To nam bo namreč pomagalo razumeti, kar sledi.

Lokalne shrambe

Vemo, da ima vsaka funkcija svoje spremenljivke, svoja imena. Kako to deluje, najbrž slutimo: vsaka ima svoj slovar. Do njega ne pridemo prek globals(), temveč prek locals(). Kar takoj poškilimo vanj.

>>> def f(x, y):
...     z = 13
...     print(locals())
...
>>> f(1, "Benjamin")
{'y': 'Benjamin', 'x': 1, 'z': 13}

Lokalne spremenljivke, ki jih pozna f, so torej x in y, ki ju je dobila kot argument, in z, ki jo je ustvarila kasneje.

Funkcije imajo dostop tudi do globalnih spremenljivk. Tudi te lahko vidimo.

>>> def f(x, y):
...     z = 13
...     print(locals())
...     print(globals())
...
>>> f(1, "Benjamin")
{'y': 'Benjamin', 'x': 1, 'z': 13}
{'a': 42, 'b': 43, 'ime': 'Benjamin', 'f': <function f at 0x10a3950c8>,
's': [1, 2, 3], 'spremenljivke': {...}, '__name__': '__main__'}

Vse torej deluje podobno kot prej, le da vsaka funkcija uporablja svoj lokalni slovar.

(Vsaj v teoriji. V praksi Python od verzije 2.nekaj naprej goljufa in lokalnih spremenljivk ne shranjuje v slovarje, temveč z njimi dela na nek drug, hitrejši način. Še vedno ima funkcijo locals(), ki se dela, da tak slovar obstaja - vendar to naredi tako, da ga na hitro sestavi, kadar ga želimo videti. Da tega slovarja v resnici ni, bi se prepričali tako, da bi vanj kaj dodali. Za razliko od primera s slovarjem globals(), ki je resničen, tisto, kar dodamo v locals() ne pojavi kot lokalna spremenljivka.)

Mimogrede opazimo, da je ena od globalnih spremenljivk tudi funkcija f. To je razumljivo: funkcija je objekt in ima ime, pod katerim jo lahko prikličemo in pokličemo. Ime f smo na funkcijo vezali z def f(x, y).

Več funkcij

Kaj pa se dogaja, če imamo več funkcij, ki se kličejo med seboj? To si oglejmo na klasičnem primeru iskanja popolnih števil, ki ga običajno pokažem pri Programiranju 1.

def delitelji(n):
    d = []
    for i in range(1, n):
        if n % i == 0:
            d.append(i)
    return d

def vsota(s):
    v = 0
    for e in s:
        v += e
    return v

def popolno(n):
    return vsota(delitelji(n)) == n

def popolna_do(n):
    p = []
    for i in range(2, n + 1):
        if popolno(i):
            p.append(i)
    return p

print(popolna_do(1000))

V program postavimo prekinitveno točko in mu počasi sledimo do nekje znotraj funkcije delitelji. Potem pa poglejmo, kaj PyCharm kaže levo spodaj.

Ignorirajte execfile in vse, kar je pod njim; to je PyCharmova šara. Naš program se začne pri module, kakor PyCharm iz nekega razloga imenuje naš program. Ta je v 27. vrstici skočil v funkijo popolna_do. Ta je v 23. vrstici skočila v funkcijo popolno, ki je v 17. vrstici skočila v funkcijo delitelji. Python si vse te stvari zapomni - saj si jih mora, da se bo znal vračati čez vse te klice.

Če pogledamo z druge strani: katera vrstica programa se trenutno izvaja? Četrta vrstica, znotraj funkcije delitelji. No, klic funkcije delitelji je prekinil izvajanje v 17. vrstici, ko je šlo za funkcijo popolno. Ko bo funkcija delitelji končana, se bo izvajanje programa nadaljevalo v 17. vrstici. In ko bo končana funkcija popolno, se bo izvajanje nadaljevalo v 23. vrstici, kjer je bilo prekinjeno zaradi klica te funkcije. (V resnici si Python ne zapomni vrstice, kjer se je izvajanje "prekinilo", ker je skočil v neko funkcijo; zapomniti si mora kar mesto znotraj vrstice - oziroma nekaj podobnega.)

Tako kot Python zlaga spremenljivke (no, imena in vrednosti) v slovar, zlaga te podatke o izvajanju v nekakšen seznam. Samo "nekakšen seznam" iz dveh razlogov. Kot prvo, ne gre za Pythonov seznam - za razliko od globals, ki je v resnici čisto običajen Pythonov slovar. Drugo, beseda "seznam" ima v terminologiji podatkovnih struktur določen pomen in tule ne bi bila prav ustrezna. V resnici se tej stvari, v katero Python sklada podatke o tem, od kod je skočil kam (da bi vedel, kam se mora vrniti), reče sklad. PyCharm nam torej kaže sklad klicev.

Na skladu niso samo podatki o tem, kam se je potrebno vrniti. Python poleg tega na skladu shranjuje tudi slovarje lokalnih spremenljivk. (Spet z opombo, da novejše verzije Pythona nekoliko goljufajo; končni učinek je vseeno isti, kot da bi to počel.) Če v PyCharmu klikamo po skladu, nam le-ta kaže lokalne spremenljivke funkcije, na katero smo kliknili - točneje, lokalne spremenljivke, ki so znane v trenutku, ko se je izvajanje te funkcije "prekinilo" zaradi klica.

Če je koga pri rekurziji motilo, da funkcija povozi svoje lastne spremenljivke: ne, ne povozi jih. Ob vsakem klicu se na sklad postavi nov slovar.

Pravila za iskanje imen

Pythonovi programi so razdeljeni v "bloke". Bloki so funkcije, razredi in "moduli" (zakaj je modul pod narekovaji, bomo videli). Python vsako ime najprej išče v lokalnem bloku (torej znotraj funkcije ali razreda - kako lahko išče nekaj znotraj razreda, bomo še videli). Če imena ni tam, pogleda v globalni blok. Če ga ni niti tam, javi napako.

n = 12
def f():
    print(n)

Kaj se izpiše, če pokličemo f? 12, seveda. Ker n-ja ni med lokalnimi imeni, ga najde med globalnimi.

def f():
    print(m)

Če pokličemo to funkcijo, se (ob predpostavki, da niti v globalnem prostoru ni nobenega m-ja) sproži napaka

NameError: name 'm' is not defined

Kaj pa tole?

n = 12
def f():
    print(n)
    n = 5

n = 5, ki sledi print-u, ne bi smel ničesar spremeniti. Da, lahko bi bilo narejeno tako. Vendar ni. Python goljufa in spremenljivke v resnici niso zbrane v slovarju locals. Da bi programi tekli hitreje, Python odkrije imena že med prevajanjem funkcije (če kdo misli, da se programi v Pythonu za razliko od Javanskih in Cjevskih ne prevajajo, se moti) ter pripravi prostor zanje na nek učinkovitejši način. Posledično so vsa imena, ki jih prirejamo vrednost znotraj funkcije, lokalna imena. Python ve, da so lokalna in jih išče v lokalnem prostoru. Če torej pokličemo gornjo funkcijo, dobimo:

UnboundLocalError: local variable 'n' referenced before assignment

Sporočilo je jasno - n poskušamo uporabljati ("referencirati") preden jih karkoli priredimo. Razumemo pa tudi ime napake, UnboundLocalError - gre za lokalno ime, ki pa še ni na nič vezano.

Obvozi mimo pravil

Napišimo funkcijo indeks(s, x), ki dobi seznam s in vrednost x ter vrne indeks tega elementa v seznamu. Recimo, da smemo predpostaviti, da seznam v resnici vsebuje ta element. Pri tem ne bomo uporabili očitne rešitve -

def indeks(s, x):
    return s.index(x)

Ali skoraj enako očitne (a seveda bistveno počasnejše):

def indeks(s, x):
    for i, e in enumerate(s):
        if e == x:
            return i

Naredili bomo nekaj neumnega: v neskončni zanki bomo naključno žrebali indekse, preverili ali je element s tem indeksom enak iskanemu, in ga v tem primeru vrnili.

def indeks(s, x):
    from random import randint
    while True:
        i = randint(0, len(s) - 1)
        if s[i] == x:
            return i

Mimogrede, kako smo uvozili randint? Lahko po takšnem uvozu uporabljamo modul random? Ne: from random import randint veže ime randint na istoimensko funkcijo iz tega modula. Nič drugega. Modul random je sicer v resnici uvožen, vendar ne moremo do njega (razen, če poznamo kakšne stranske poti :), ker nanj ni vezano nobeno ime.

Lahko po takšnem uvozu uporabljamo funkcijo randint tudi izven funkcije? Ne. Ime randint je vezano samo lokalno. Tako kot, recimo i - tudi tega vidimo le znotraj funkcije.

Recimo, da nas zanima, kako slaba je ta ideja: radi bi šteli, kolikokrat je bilo potrebno ponoviti zanko. Kumulativno: recimo, da bomo večkrat poklicali funkcijo. In funkcija naj še vedno vrača le indeks.

poskusov = 0

def indeks(s, x):
    from random import randint
    while True:
        poskusov += 1
        i = randint(0, len(s) - 1)
        if s[i] == x:
            return i

To ne deluje, javi pa prav zanimivo napako

poskusov += 1
UnboundLocalError: local variable 'poskusov' referenced before assignment

Prirejanje poskusov += 1 moramo dojemati kot poskusov = poskusov + 1. To pomeni, da je poskusov lokalna spremenljivka - saj ji znotraj funkcije prirejamo vrednost. Python poskuša izračunati poskusov + 1, ugotovi, da ime poskusov še ni vezano na noben objekt in ... konec zabave.

To uredimo tako Pythonu povemo, da je poskusov globalna spremenljivka. Nekam (zanimivo: kamorkoli) v funkcijo napišemo global poskusov. Zdaj bo Python vedno iskal ime poskusov (ali, če bo šlo za vezavo, vezal poskusov) v globalni imenski prostor.

poskusov = 0

def indeks(s, x):
    from random import randint
    global poskusov
    while True:
        poskusov += 1
        i = randint(0, len(s) - 1)
        if s[i] == x:
            return i


s = list(range(1000))
for i in range(100):
    indeks(s, 0)
print(poskusov / 100)

Če stokrat poiščemo ničlo in izpišemo poprečno število potrebnih poskusov, dobimo nekaj zanimivega.

999.84

Številka je sumljivo blizu 1000. Ne bo vedno tako blizu, vedno pa bo ... kar blizu. Vendar to sodi v nek drug predmet.

No, nihče nam ne brani raziskovati tega fenomena tudi tu. Zaprimo celoten program v funkcijo, ki ji podamo velikost seznama (npr. 1000) in vrne število poskusov, ki je v poprečju potrebno, da najdemo element 0 (ki je sicer vedno čisto na začetku, vendar funkcija tega žal ne ve :).

def izmeri(n):
    poskusov = 0

    def indeks(s, x):
        global poskusov
        from random import randint
        while True:
            poskusov += 1
            i = randint(0, len(s) - 1)
            if s[i] == x:
                return i

    s = list(range(n))
    for i in range(100):
        print(indeks(s, 0))
    return poskusov / 100

print(izmeri(1000))

To ne deluje.

Zato, ker ne smemo pisati funkcij znotraj funkcij? Ne, to ni nič neobičajnega. def indeks veže ime indeks na funkcijo. Vezava je lokalna: ime indeks je znano znotraj imenskega prostora funkcije izmeri.

Če pozorno pogledamo, imamo zdaj opravka s tremi imenskimi prostori.

En je globalni: v njem je pravzprav le ena reč (poleg avtomatske navlake), namreč funkcija izmeri.

Poleg tega imamo še dve funkciji, torej dva imenska prostora. Imenski prostor funkcije izmeri vsebuje ... no, saj nam funkcija sama pove, če

  • pred return poskusov / 100 dodamo print(locals())
  • pobrišemo poskusov += 1, da program ne bo javil napake, še preden sploh kaj izpiše,
  • klic izmeri(1000) spremenimo v izmeri(5), da izpis ne bo predolg.

Izpiše se

{'i': 99, 'indeks': <function izmeri.<locals>.indeks at 0x103c39950>,
 's': [0, 1, 2, 3, 4], 'poskusov': 507, 'n': 5}

Tretji imenski prostor je imenski prostor funkcije indeks. Če pred return dodamo print(globals()), izvemo

{'randint': <bound method Random.randint of <random.Random object at 0x7faad180c618>>,
 's': [0, 1, 2, 3, 4], 'i': 0, 'x': 0}

V katerem imenskem prostoru, z vidika indeks je ime poskusov? V lokalnem ne. V globalnem pa tudi ne. Lahko potem sploh pridemo do tega imena? Lahko.

def izmeri(n):
    poskusov = 0

    def indeks(s, x):
        nonlocal poskusov
        from random import randint
        while True:
            poskusov += 1
            i = randint(0, len(s) - 1)
            if s[i] == x:
                return i

    s = list(range(n))
    for i in range(100):
        indeks(s, 0)
    return poskusov / 100

print(izmeri(5))

Definicija nonlocal pomeni, da imena ne želimo iskati med lokalnimi imeni. Pač pa ga išče v najbližjem nelokalnem prostoru. Če imamo funkcijo znotraj funkcije znotraj funkcije ... nadaljuje iskanje navzven, dokler ne pride do imena.

Ne spreglejte, da gre tu za iskanje glede na bloke, ne glede na klice. Če ima neka funkcija spremenljivko i in pokliče drugo funkcijo, le-ta ne more dostopati do i-ja, kot da bi bil le-ta nelokalna spremenljivka.

def g():
    nonlocal i
    print(i)

def f():
    i = 12
    g()

f()

Tole ne javi le napake med izvajanjem: Python ob tem programu javi celo sintaktično napako! Ob prevajanju programa ugotovi, da i-ja ne more vezati na nobeno nelokalno spremenljivko.

Tudi tole ne deluje:

i = 0

def g():
    nonlocal i
    print(i)

Nelokalna spremenljivka mora biti lokalna spremenljivka funkcije, znotraj katere je definirana g. Globalni i ne more biti nelokalni.

Globalne in nelokalne spremenljivke niso nekaj, kar bi smel človek uporabljati rutinsko in brez slabe vesti. Posebej za globalne spremenljivke potrebujemo dober izgovor.

Imenski prostori modulov

Recimo, da smo zgornji blok kode za računanje popolnih števil shranili v datoteko stevila.py. Odprimo Python in napišimo import stevila.

Zgodi se nekaj zanimivega. Izpišejo se vsa popolna števila do 1000.

>>> import stevila
[6, 28, 496]

Modul stevila se sicer obnaša kot vsi spodobni moduli, ima, recimo, funkcijo delitelji, ki jo lahko pokličemo, kot se pač kliče funkcije v modulih.

>>> stevila.delitelji(28)
[1, 2, 4, 7, 14]

Tega smo vajeni, to ni nič takšnega: do funkcij (in drugih reči), ki so v modulih, pridemo tako, da napišemo ime modula, piko, in ime funkcije (ali druge reči).

Zakaj je izpisal vsa popolna števila, je sicer jasno: zaradi printa na koncu. Da bomo o tem čisto prepričani, dodajmo na konec datoteke stevila.py še

print("Dober dan")

Zaprimo Python, ga ponovno odprimo in ponovno uvozimo modul. (To je potrebno zato, ker Python vedno uvozi modul le enkrat, ob ponovnih uvažanjih pa se samo dela, da ga je ponovno uvozil. Točneje: veže ime na že uvoženi modul.)

>>> import stevila
[6, 28, 496]
Dober dan

Zdaj pa namesto print("Dober dan") dodajmo nekaj zanimivejšega: print(globals()). Modul naj izpiše globalne spremenljivke.

Izpiše se nek grozen slovar. Par stvari sem pobrisal (no, ena od teh parih stvari je bila dolga nekaj zaslonov); pustil sem le, kar je zanimivo.

>>> import stevila
[6, 28, 496]
{'delitelji': <function delitelji at 0x101229378>,
'popolno': <function popolno at 0x1012e4158>,
'popolna_do': <function popolna_do at 0x1012e47b8>,
'vsota': <function vsota at 0x1012e46a8>,
'__name__': 'stevila'}

In zdaj napišimo

>>> globals()
{'stevila': <module 'stevila' from '/Users/janezdemsar/py2/01 - imenski prostori/stevila.py'>,
'__name__': '__main__'}

Obstajata torej dva globals(): modul ima svoje globalne spremenljivke, "program", ki ga poganjamo (ali ukazna vrstica), pa svoje.

Točneje: vsak modul ima svoj globalni imenski prostor.

Še točneje: "glavni program" je v bistvu modul.

Vsak modul ima svoje globalne spremenljivke. Ko modul stevila pokliče globals(), dobi svoje globalne spremenljivke; prav zato lahko znotraj modula stevila, recimo v funkciji popolno, pokliče kar delitelji in ne stevila.delitelji (za kar bi moral, za začetek, uvoziti modul stevila, torej samega sebe, kar se morda ne bi dobro končalo).

Ko "program" pokliče globals(), dobi svoje globalne spremenljivke.

Ena od globalnih spremenljivk, ki se je pojavila sama od sebe, je __name__. Ta je imela doslej vrednost __main__. Poglejte, kakšno vrednost ima v globalnih spremenljivkah modula stevila:

{'delitelji': <function delitelji at 0x101229378>,
'popolno': <function popolno at 0x1012e4158>,
'popolna_do': <function popolna_do at 0x1012e47b8>,
'vsota': <function vsota at 0x1012e46a8>,
'__name__': 'stevila'}

Spremenljivka __name__ vsebuje ime modula, torej 'stevila'.

In zdaj še enkrat poglejmo "prava" globalna imena.

{'stevila': <module 'stevila' from '/Users/janezdemsar/py2/01 - imenski prostori/stevila.py'>,
'__name__': '__main__'}

Glavni program je torej pravzaprav le modul z imenom __main__. Lahko ga celo uvozimo - kar je sicer neumno, vendar se da.

>>> x = 5
>>> import __main__
>>> __main__.x
5

Pika, ., je operator za dostop do imen znotraj imenskih prostorov modulov (in razredov in objektov).

Kje so shranjena imena znotraj imenskih prostorov modulov? No, to že vemo: v slovarju. Do tega slovarja pridemo z globals() - vendar le, če pokličemo globals() znotraj modula, katerega imenski prostor nas zanima.

Vsak modul ima poleg vseh najrazličnejših funkcij in drugih reči, ki jih izvaža, tudi slovar z imenom __dict__. Ta slovar ni nič drugega kot slovar njegovih globalnih imen - se pravi, ime, za katera ta modul meni, da so globalna.

 >>> stevila.__dict__
{'delitelji': <function delitelji at 0x101229378>,
'popolno': <function popolno at 0x1012e4158>,
'popolna_do': <function popolna_do at 0x1012e47b8>,
'vsota': <function vsota at 0x1012e46a8>,
'__name__': 'stevila'}

Kar je za nas stevila.__dict__ je za modul stevila slovar, ki ga vrača globals().

Ko napišemo stevila.delitelji, zahtevamo tisto, kar je v slovarju "globalnih" imen modula stevila shranjeno pod ključem delitelji.

>>> __main__.__dict__ is globals()
True
Zadnja sprememba: ponedeljek, 14. marec 2016, 20.57