Gli argomenti vengono passati per incarico . La logica alla base è duplice:
- il parametro passato è in realtà un riferimento a un oggetto (ma il riferimento viene passato per valore)
- alcuni tipi di dati sono mutabili, ma altri no
Così:
Se passi un oggetto mutabile in un metodo, il metodo ottiene un riferimento a quello stesso oggetto e puoi mutarlo per la gioia del tuo cuore, ma se ricolleghi il riferimento nel metodo, l'ambito esterno non ne saprà nulla e dopo hai finito, il riferimento esterno punterà comunque verso l'oggetto originale.
Se passi un oggetto immutabile a un metodo, non puoi ancora ricollegare il riferimento esterno e non puoi nemmeno mutare l'oggetto.
Per renderlo ancora più chiaro, facciamo alcuni esempi.
Elenco: un tipo modificabile
Proviamo a modificare l'elenco che è stato passato a un metodo:
def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)
outer_list = ['one', 'two', 'three']
print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)
Produzione:
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
Poiché il parametro passato è un riferimento outer_list
, non una copia di esso, possiamo usare i metodi dell'elenco di mutazioni per cambiarlo e far sì che le modifiche si riflettano nell'ambito esterno.
Ora vediamo cosa succede quando proviamo a modificare il riferimento passato come parametro:
def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)
outer_list = ['we', 'like', 'proper', 'English']
print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)
Produzione:
before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']
Poiché il the_list
parametro è stato passato per valore, l'assegnazione di un nuovo elenco non ha avuto alcun effetto che il codice esterno al metodo potesse vedere. L' the_list
era una copia del outer_list
riferimento, e abbiamo dovuto the_list
puntare a una nuova lista, ma non c'era modo di cambiare doveouter_list
punta.
String - un tipo immutabile
È immutabile, quindi non c'è nulla che possiamo fare per modificare il contenuto della stringa
Ora proviamo a cambiare il riferimento
def try_to_change_string_reference(the_string):
print('got', the_string)
the_string = 'In a kingdom by the sea'
print('set to', the_string)
outer_string = 'It was many and many a year ago'
print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)
Produzione:
before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago
Ancora una volta, poiché il the_string
parametro è stato passato per valore, l'assegnazione di una nuova stringa non ha avuto alcun effetto sul codice esterno al metodo. L' the_string
era una copia del outer_string
riferimento, e abbiamo dovuto the_string
puntare a una nuova stringa, ma non c'era modo di cambiare dove outer_string
punta.
Spero che questo chiarisca un po 'le cose.
MODIFICARE: è stato notato che questo non risponde alla domanda che @David originariamente ha posto, "C'è qualcosa che posso fare per passare la variabile per riferimento reale?". Lavoriamo su quello.
Come possiamo aggirare questo?
Come mostra la risposta di @ Andrea, potresti restituire il nuovo valore. Questo non cambia il modo in cui le cose vengono passate, ma ti consente di ottenere le informazioni che desideri restituire:
def return_a_whole_new_string(the_string):
new_string = something_to_do_with_the_old_string(the_string)
return new_string
# then you could call it like
my_string = return_a_whole_new_string(my_string)
Se davvero volessi evitare di usare un valore di ritorno, potresti creare una classe per conservare il tuo valore e passarlo nella funzione o usare una classe esistente, come un elenco:
def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
new_string = something_to_do_with_the_old_string(stuff_to_change[0])
stuff_to_change[0] = new_string
# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)
do_something_with(wrapper[0])
Anche se questo sembra un po 'ingombrante.