[python] Nesrozumitelne prirazovaci prikazy zamichane mezidefinice standalone funkci v modulu

Petr Prikryl PrikrylP na skil.cz
Pondělí Červenec 9 10:35:05 CEST 2007


Petra Javornicka
> 
> [...] To je jasny, ale porad nevim, odkud bere interpretr 
> odlisit informaci, aby mohl
> g=D   # pointer
> d=D(f) # taky pointer :-O
> d=D(n) # dereference - call
> 
> U syntaxe dekoratoru s prefixem '@' se to zda byt jasny. 
> Ale u funkce utrousene nekde mezi definicemi?

Za klíčové považuji pochopit, jak funguje dekorování funkce
bez použití @. Je to historicky starší, funguje to úplně 
stejně, jen se to líp zapisuje a čte (což je důležité).

To "u funkce utroušené mezi definicemi" je taky důležité
pro pochopení. Při zápisu bez @ lze dekorování provést
až za koncem definice funkce. Pythonovský zdroják se 
zpracovává sekvenčně shora dolů a ihned se provádí. 
Když se narazí na definici funkce, vytváří se vnitřní
objekt funkce (ekvivalent kompilace) a vytvoří se reference
pojmenovaná předepsaným identifikátorem funkce. Ten 
identifikátor ale od té doby není nijak výsadní
(snad jen, že je v objektu funkce textově zachycen
pro účely ladění nebo pro jiné introspektivní použití).

Pokud mám například

def mojeFunkce(n):
    return n * 5

Pak si ji můžu kdykoliv přejmenovat:

f = mojeFunkce

... protože identifikátor mojeFunkce je jen pojmenovaná
reference na vnitřní objekt funkce (objekt v technickém
smyslu, tj. podoba, do které je definice funkce zpracována).

Můžu si pak ověřit, že příkazy vypíšou stejnou identifikaci

print id(mojeFunkce)
print id(f)

... která je v momentální implementaci Pythonu totožná
s adresou (objektu) funkce.

Za předpokladu, že reference odkazuje na "callable"
objekt, chápe se za volání zápis, kdy za referenci
uvedu kulaté závorky (s argumenty nebo bez).

Když se následující příklad uloží do souboru d.py
----------------------------------------------------
def mojeFunkce(n):
    return n * 5
    
f = mojeFunkce

print id(mojeFunkce)
print id(f)
print mojeFunkce(3)
print f(3)

def trivialniDekorator(fce):
    return fce
    
f = trivialniDekorator(mojeFunkce)

print id(mojeFunkce)
print id(f)
print mojeFunkce(3)
print f(3)

def prazdnyDekorator(fce):
    def obalujici_funkce(n):
        return fce(n)
    return obalujici_funkce    

f = prazdnyDekorator(mojeFunkce)

print id(mojeFunkce)
print id(f)
print mojeFunkce(3)
print f(3)

def plusJedna(fce):
    def plusOne(n):
        return fce(n) + 1
    return plusOne
        
# Znovu pouziju identifikator mojeFunkce. Od teto chvile se
# zahodi reference na puvodni objekt funkce, coz ale neznamena,
# ze by objekt puvodni funkce zanikl.

mojeFunkce = plusJedna(mojeFunkce)         
print id(mojeFunkce)
print mojeFunkce(3)

# Pri selhani volane funkce se vypise trasovaci zprava, ktera mimo 
# jine uvadi originalni identifikace funkce, ktera je sice stejna, jako 
# pojmenovana reference, ale novy identifikator jiz odkazuje na funkci 
# na jine adrese.

print id(mojeFunkce)
mojeFunkce(None)
----------------------------------------------------

Pak se po zavolání programu vypíše následující

C:\tmp>python d.py
17722800
17722800
15
15
17722800
17722800
15
15
17722800
17722992
15
15
17723120
16
17723120
Traceback (most recent call last):
  File "d.py", line 52, in <module>
    mojeFunkce(None)
  File "d.py", line 35, in plusOne
    return fce(n) + 1
  File "d.py", line 2, in mojeFunkce
    return n * 5
TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'


pepr


Další informace o konferenci Python