View Full Version : [Python] Oggetti mutable di default
Ciao a tutti, sto imparando il Python seguendo questa guida, ho appena iniziato:
http://www.python.it/doc/Howtothink/Howtothink-html-it/index.htm
In più, sto seguendo questo link:
http://zephyrfalcon.org/labs/python_pitfalls.html
Nell' esempio 5, viene mostrato cosa succede in questo caso:
def popo(x=[]):
... x.append(666)
... print x
...
>>> popo([1, 2, 3])
[1, 2, 3, 666]
>>> x = [1, 2]
>>> popo(x)
[1, 2, 666]
>>> x
[1, 2, 666]
E l'output mi torna.
Ma come mai se scrivo questo:
def prova(x=1):
x = x +1
print x
x non viene incrementato ad ogni chiamata di prova senza argomenti? :confused: Cioè, mi aspetterei, alla luce dell' esempio sopra, che chiamando due volte prova() x valga 3, ma invece vale sempre 2.
Ryuzaki_Eru
28-12-2009, 13:40
Se definisci una funzione in modo che si aspetti un argomento e poi la chiami senza parametri lasciando l'argomento predefinito, è normale che non può fare quello che vorresti. Ogni volta che entri nella funzione la situazione delle variabili all'interno si "resetta". Di conseguenza è normale che ottieni sempre due: Python valuta l'espressione a destra (vede l'uno che assegna ad x e gli aggiunge x che in quel momento è 1) e quindi ottieni x = 2.
Inoltre non capisco perchè passi i parametri come keyword. E' bruttissimo quel codice.
Se definisci una funzione in modo che si aspetti un argomento e poi la chiami senza parametri è normale che non può fare quello che vorresti. Ogni volta che entri nella funzione la situazione delle variabili all'interno si "resetta". Di conseguenza è normale che ottieni sempre due: Python valuta l'espressione a destra (vede l'uno che assegna ad x e gli aggiunge x che in quel momento è 1) e quindi ottieni x = 2.
Si ok, ma leggendo nel link che ho postato e seguendo l'esempio all'inizio dice:
The default argument is bound *once*, when the function is *created*, not when it's called
In questo caso l'argomento di default è x e, a leggere qua, dovrebbe essere creato una volta sola.
Inoltre non capisco perchè passi i parametri come keyword. E' bruttissimo quel codice.
Non ho capito..
Si ok, ma leggendo nel link che ho postato e seguendo l'esempio all'inizio dice:
The default argument is bound *once*, when the function is *created*, not when it's called
In questo caso l'argomento di default è x e, a leggere qua, dovrebbe essere creato una volta sola.
Non ho capito..
Perchè i numeri sono oggetti immutabili in Python
cdimauro
28-12-2009, 13:59
Ciao a tutti, sto imparando il Python seguendo questa guida, ho appena iniziato:
http://www.python.it/doc/Howtothink/Howtothink-html-it/index.htm
In più, sto seguendo questo link:
http://zephyrfalcon.org/labs/python_pitfalls.html
Nell' esempio 5, viene mostrato cosa succede in questo caso:
def popo(x=[]):
... x.append(666)
... print x
...
>>> popo([1, 2, 3])
[1, 2, 3, 666]
>>> x = [1, 2]
>>> popo(x)
[1, 2, 666]
>>> x
[1, 2, 666]
E l'output mi torna.
Ma come mai se scrivo questo:
def prova(x=1):
x = x +1
print x
x non viene incrementato ad ogni chiamata di prova senza argomenti? :confused: Cioè, mi aspetterei, alla luce dell' esempio sopra, che chiamando due volte prova() x valga 3, ma invece vale sempre 2.
Questo succede perché le liste sono oggetti "mutabili", per cui in qualunque punto del codice vengono modificati, tali modifiche si rifletteranno in tutte le variabili che contengono dei loro riferimenti.
Ecco perché l'output "ti torna" con l'esempio delle liste.
Ciò ovviamente non avviene con gli oggetti che sono immutabili, come gli interi, le stringhe, ecc.
Ed ecco perché non ti torna il secondo esempio. Questo perché il parametro x della funzione prova riceve ad ogni chiamata un valore nuovo, che viene incrementato, stampato, ma poi distrutto alla fine della chiamata.
Attenzione che, per quanto detto, possono esserci problemi quando si definisco funzioni aventi parametri predefiniti con assegnati oggetti mutabili, perché tale valore viene calcolato una sola volta, alla definizione della funzione, per poi essere riutilizzato in tutti gli altri casi (si può pensare come a una sorta di variabile statica in linguaggi come C & co). Esempio:
def popo2(x=[]):
x.append(666)
return x
print popo2()
print popo2([1, 2])
print popo2()
;)
Ryuzaki_Eru
28-12-2009, 14:16
In questo caso l'argomento di default è x e, a leggere qua, dovrebbe essere creato una volta sola.
Infatti viene creato un volta sola, ma come ti è stato detto gli interi sono tipi immutabili. Quando tu crei la funzione con parametro x = 1 hai referenziato alla variabile x (che esiste solo nello scope della funzione) l'oggetto 1. Questo non cambierà mai. Ogni volta che chiami prova() è come se la chiamassi con prova(1) e di conseguenza otterrai sempre 2.
Se invece nel tuo primo esempio con le liste provi a chiamare la funzione senza parametri vedrai che otterrai il medesimo risultato del codice scritto da cdimauro.
Esempio:
>>> a = 1
>>> b = a
>>> a += 1
>>> a
2
>>> b
1
>>> a = [1,2]
>>> b = a
>>> a[0] = "ciao"
>>> a
['ciao', 2]
>>> b
['ciao', 2]
def popo2(x=[]):
x.append(666)
return x
print popo2()
print popo2([1, 2])
print popo2()
;)
Perfetto, adesso ho capito! Grazie mille a tutti :)
Però ad esempio, se faccio un ciclo di 100 iterazioni, in cui incremento di 1 una variabile intera ad ogni iterazione, vengono creati e distrutti continuamente 100 oggetti interi?
CPython ha già pronti in memoria un pò di int :D
Se non ricordo male, da -5 a 256
CPython ha già pronti in memoria un pò di int :D
Se non ricordo male, da -5 a 256
Come mai hanno implementato questo comportamento con gli incrementi su oggetti non mutable?
Ryuzaki_Eru
28-12-2009, 19:11
Non mutabile non significa che non ci puoi fare nulla. Semplicemente Python crea un nuovo oggetto invece di modificarne uno esistente.
Il motivo per il quale ha un pò di interi "messi da parte" è dovuto al suo meccanismo di "chace" interno. che tiene "da parte" i valori più piccoli per poterli prendere più velocemente in caso di necessità.
Ehm,
ho un altro piccolo quesito. Se scrivo:
a = 3
def prova(x):
x+=1
prova(a)
Giustamente, a non cambia di valore.
Ma allora, perché se scrivo così:
lista = [10]
def prova(elem):
elem[0]+=1
prova(lista)
Perché così invece il primo valore di lista viene incrementato? Il tipo dell'elemento lista[0] è sempre un intero, quindi dovrebbe comportarsi come l'esempio precedente. Oppure conta solo il tipo del contenitore, quindi tipo lista e quindi mutable?
Ehm,
ho un altro piccolo quesito. Se scrivo:
a = 3
def prova(x)
x+=1
prova(a)
Giustamente, a non cambia di valore.
Ma allora, perché se scrivo così:
lista = [10]
def prova(elem):
elem[0]+=1
prova(lista)
Perchè così invece il primo valore di lista viene incrementato? Il tipo dell'elemento elem[0] è sempre un intero, quindi dovrebbe comportarsi come l'esempio precedente. Oppure conta solo il tipo del contenitore, quindi lista e quindi mutable?
Scusate l'edit doppio..
Ryuzaki_Eru
29-12-2009, 15:56
Come ti è stato detto le liste sono mutabili. Se passi una lista alla funzione viene modificato direttamente l'oggetto. Se passi un intero viene creato un *nuovo* oggetto che all'uscita della funzione viene distrutto, di conseguenza la variabile a rimane intatta.
cdimauro
30-12-2009, 07:28
CPython ha già pronti in memoria un pò di int :D
Se non ricordo male, da -5 a 256
Sì, esatto. E tiene in memoria anche:
- la stringa vuota;
- tutte le stringhe dotate di un solo carattere;
- tutte le stringhe unicode dotate di un solo carattere con codice fino U00FF (i primi 256 elementi);
- la tupla vuota;
- gli oggetti None, True, False ed Ellipsis (...).
Il tutto, come diceva giustamente Ryu, per fare caching.
Tra poco avrà in memoria anche le stringhe '__enter__' ed '__exit__', ma non nella versione ufficiale. :fagiano:
Ehm,
ho un altro piccolo quesito. Se scrivo:
a = 3
def prova(x)
x+=1
prova(a)
Giustamente, a non cambia di valore.
Ma allora, perché se scrivo così:
lista = [10]
def prova(elem):
elem[0]+=1
prova(lista)
Perchè così invece il primo valore di lista viene incrementato? Il tipo dell'elemento elem[0] è sempre un intero, quindi dovrebbe comportarsi come l'esempio precedente. Oppure conta solo il tipo del contenitore, quindi lista e quindi mutable?
Scusate l'edit doppio..
Come diceva Ryu, stai referenziando un elemnto della lista che hai passato alla funzione. Più precisamente, le azioni che Python compie con questo:
elem[0] += 1
pezzo di codice sono effettivamente le seguenti:
prova[0] = prova[0] + 1
Perché il parametro lista punta esattamente all'oggetto prova, che è una lista. E tu stai dicendo a Python di prendere il contenuto dell'elemento che sta nella posizione 0, sommargli 1, e mettere il risultato alla medesima posizione.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.