Argumenty są przekazywane przez przypisanie . Uzasadnienie tego jest dwojakie:
- przekazany parametr jest tak naprawdę referencją do obiektu (ale referencja jest przekazywana przez wartość)
- niektóre typy danych są zmienne, ale inne nie
Więc:
Jeśli przekażesz zmienny obiekt do metody, metoda otrzyma odwołanie do tego samego obiektu i możesz zmutować go ku uciesze twojego serca, ale jeśli ponownie powiążesz odniesienie w metodzie, zakres zewnętrzny nie będzie o tym nic wiedział, a po tym gdy skończysz, odniesienie zewnętrzne nadal będzie wskazywać na oryginalny obiekt.
Jeśli przekażesz niezmienny obiekt do metody, nadal nie możesz ponownie powiązać zewnętrznego odniesienia, a nawet nie możesz mutować obiektu.
Aby było jeszcze bardziej jasne, podajmy kilka przykładów.
Lista - typ zmienny
Spróbujmy zmodyfikować listę, która została przekazana do metody:
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)
Wynik:
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
Ponieważ przekazany parametr jest odniesieniem do outer_list
, a nie jego kopią, możemy użyć metod listy mutacji, aby go zmienić, a zmiany odzwierciedlić w zewnętrznym zakresie.
Zobaczmy teraz, co się stanie, gdy spróbujemy zmienić odwołanie, które zostało przekazane jako parametr:
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)
Wynik:
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']
Ponieważ the_list
parametr został przekazany przez wartość, przypisanie mu nowej listy nie miało żadnego wpływu na to, że kod poza metodą mógł zobaczyć. the_list
Była kopia outer_list
odniesienia, i mieliśmy the_list
punkt do nowej listy, ale nie było sposobu, aby zmienić miejsce outer_list
spiczasty.
String - niezmienny typ
Jest niezmienny, więc nie możemy nic zrobić, aby zmienić zawartość łańcucha
Teraz spróbujmy zmienić odniesienie
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)
Wynik:
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
Ponownie, ponieważ the_string
parametr został przekazany przez wartość, przypisanie do niego nowego ciągu nie wpłynęło na to, że kod poza metodą mógł zobaczyć. the_string
Była kopia outer_string
odniesienia, i mieliśmy the_string
punkt do nowego łańcucha, ale nie było sposobu, aby zmienić miejsce outer_string
spiczasty.
Mam nadzieję, że to trochę wyjaśni.
EDYCJA: Zauważono, że to nie odpowiada na pytanie, które @David pierwotnie zadał: „Czy jest coś, co mogę zrobić, aby przekazać zmienną przez faktyczne odwołanie?” Popracujmy nad tym.
Jak sobie z tym poradzić?
Jak pokazuje odpowiedź @ Andrei, możesz zwrócić nową wartość. Nie zmienia to sposobu przekazywania rzeczy, ale pozwala uzyskać informacje, które chcesz odzyskać:
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)
Jeśli naprawdę chcesz uniknąć używania wartości zwracanej, możesz utworzyć klasę, która będzie przechowywać twoją wartość i przekaże ją do funkcji lub użyj istniejącej klasy, takiej jak lista:
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])
Chociaż wydaje się to trochę kłopotliwe.