[python] Referencia

Petr Prikryl PrikrylP na skil.cz
Pátek Únor 3 10:40:31 CET 2006


Zkusím to celé shrnout a vysvětlit po svém...

balky napsal:
> [...] pokial viem, tak ak do premennej priradim nieco,
> nepriradil som vlastne obsah tej premennej, ale iba
> odkaz (pointer) na ten objekt.
>
> >>> a = [1,2,3]
> >>> b = a
> >>> print b
> [1,2,3]
> >>> a = [3,3,3]
> >>> print a
> [3,3,3]
> >>> print b
> [1,2,3]
>
> Ako zabezpecim, aby sa pri zmene hodnoty a zmenila aj
> hodnota v b? Nie je b iba odkaz na a? Alebo je to
> odkaz na ine a?

JP reagoval:
> zapomen na to, co znas z Pascalu, C apod. Neexistuji
> zadne promenne 'a' a 'b'. Jsou to jenom dve jmena,
> kterym je prirazeny objekt. Kdyz napises b = a, tak
> obe dve jmena ukazuji na stejny objekt a jakmile
> napises a = [3,3,3], tak uz a neodkazuje na puvodni
> objekt, zatimco b ano.

Je to tak trochu napůl. V Pascalu i v C++ (a myslím, že
i v novější normě C) existuje něco, čemu se říká
reference. V Pascalu ji najdeme jen v podobě parametru
procedury/funkce předávaného odkazem (var). V C++ jde o
jeden ze základních typů a při použití se chová podobně
jako v Pythonu. Reference se od ukazatele (pointeru)
liší tím, že se při použití jména provádí automatická
dereference. Referenci se kromě toho nedá přiřadit něco
jako prázdný odkaz (nelze simulovat NULL pointer).
Použití jména proměnné typu reference se tedy jeví jako
kdybychom přejmenovali původní objekt.

V Pascalu, C/C++ a v jiných kompilovaných jazycích ale
jméno původního objektu může vystupovat i jinak, než v
Pythonu a podobných jazycích. Může být při překladu
nahrazeno přímou adresou umístění objektu. Narozdíl od
reference (a od ukazatele) takové jméno není spojeno s
přidělením dalšího paměťového prostoru za běhu
aplikace.

V Pythonu se v podstatě pracuje jen s referencemi.
Paměťový prostor, který za běhu programu zabírají, se
nachází v systémovém slovníku v podobě zachycení vztahu
'jméno': 'odkaz na objekt').


Yeti poznamenal:
> [...] Kdyby se přiřazení v Pythonu říkalo pojmenování,
> asi by se mnohé vyjasnilo.
>
> >>> a = [1,2,3]
> >>> b = a
> >>> print b
> [1,2,3]
> >>> a = [3,3,3]
>
> Tento řádek vytvoří nový objekt [3,3,3] a pojmenuje ho
> a. Na objekt [1,2,3] už pak existuje jen odkaz jménem
> b. [...] b není odkaz na žádné a; jména a a b jsou dvě
> jména téhož objektu (resp. byla, než jsi jménem a
> pojmenoval jiný objekt).
>
> Nevím, čeho se přesně snažíš dosáhnout, ale zkus se
> prostě podívat na přiřazení jako na pojmenování.

Python vazbě mezi jménem a objektem skutečně říká
"vazba" (binding). O přiřazení objektu jménu říká, že
jde o vytvoření vazby mezi jménem a objektem. V
kompilovaných jazycích se tomu říká reference, ale
Python je speciální v tom, že všechny reference
uchovává v systémových slovnících (globálním a
lokálních), kde jméno vystupuje jako klíč k hodnotě
reference.

Pro příchozí ze světa staticky kompilovaných jazyků by
se mnohé vyjasnilo spíš tím, kdyby ony jazyky běžně
pracovaly s pojmem reference (jako C++). Yetiho další
poznámku bych pak mohl pro ekvivalent v kompilovaných
jazycích přeformulovat takto:

   Tento řádek vytvoří nový objekt [3,3,3] a referenci
   přiřadí do proměnné a. Tím se původní reference na
   objekt [1,2,3] přepíše novou hodnotou. Na původní
   objekt [1,2,3] se pak dostaneme jen přes referenci
   b. [...] b není reference na (refernci) a; a i b
   jsou zpočátku dvě reference na tentýž objekt
   seznamu.


Jirka Vít šikovně navedl na použití zabudované funkce
id(), která v současných implementacích Pythonu vrací
adresu skutečného umístění objektu:

> >>> a=[1,2,3]
> >>> b = a
> >>> id(a)
> 9596640
> >>> id(b)
> 9596640

Snad by pro pochopení pomohlo, kdyby se přiřazení
objektu [3, 3, 3] provedlo na dva kroky. Nejdříve jej
pojmenujeme c (pythonovský ekvivalent přiřazení
reference) a pak hodnotě b přiřadíme hodnotu c. Můžeme
uvažovat, že jména jsou "proměnné v Pascalovském
smyslu", kterým přiřazujeme hodnoty identifikací 
skutečných objektů a při použití jmen se provádí
automatické zpřístupnění těchto objektů:

>>> a = [1, 2, 3]
>>> id(a)
9592464
>>> b = a
>>> id(b)
9592464
>>> c = [3, 3, 3]
>>> id(c)
9591744
>>> b = c
>>> id(b)
9591744

Tomáš Fujtalar správně poznamenal, že

> Ve Vasem pripade jste ale do a priradil novy objekt,
> o cemz zakonite objekt b "nema paru".

a přidal...
> nebo muzete obsah celeho seznamu a prepsat novym:
> >>> a[:] = [4,5,6,7]
> >>>print b
> [4,5,6,7]

Stejný obrat použil i Jan Martínek a píše:
> a nadále platí, že id(a) == id(b)
 
Tenhle obrat je trochu "na bednu", protože jeho
fungování je v referenční příručce (6.3 Assignment
statements) popsán trochu komplikovaně (ono to asi
jednodušeji popsat nejde):

    If the target is a slicing: The primary expression
    in the reference is evaluated. It should yield a
    mutable sequence object (such as a list). The
    assigned object should be a sequence object of the
    same type. Next, the lower and upper bound
    expressions are evaluated, insofar they are
    present; defaults are zero and the sequence's
    length. The bounds should evaluate to (small)
    integers. If either bound is negative, the
    sequence's length is added to it. The resulting
    bounds are clipped to lie between zero and the
    sequence's length, inclusive. Finally, the sequence
    object is asked to replace the slice with the items
    of the assigned sequence. The length of the slice
    may be different from the length of the assigned
    sequence, thus changing the length of the target
    sequence, if the object allows it.
    
V našem příkladu jde o speciální případ, kdy
mechanismem slice vymezíme všechny prvky seznamu a na
základě výše popsaného chování se nahradí všemi prvky z
pravé strany přiřazení. Modifikuje se tedy původní
seznam. Lépe se to možná dá pochopit na méně speciálním
případu, kdy slice použijeme jen na část seznamu:

>>> a = [1, 2, 3, 4]
>>> a[1:3]
[2, 3]
>>> a[1:3] = [8, 9, 10]
>>> a
[1, 8, 9, 10, 4]

Ten popis chování by se dal pro tento konkrétní případ
přeložit takto:

   Na levé straně přiřazení je zápisem a[1:3] vymezen
   podseznam s prvky 2 a 3. Tato část je odstraněna a
   nahrazena prvky seznamu z pravé strany přiřazení. 
    
Snad je to teď (i mně ;) trochu jasnější...

pepr



Další informace o konferenci Python