Аргументи передаються за призначенням . Обґрунтування цього двояке:
- переданий параметр насправді є посиланням на об'єкт (але посилання передається за значенням)
- деякі типи даних є змінними, але інші - ні
Тому:
Якщо ви передаєте змінений об'єкт у метод, метод отримує посилання на той самий об’єкт, і ви можете його вимкнути на радість вашому серцю, але якщо ви відновите посилання в методі, зовнішня область про це нічого не буде знати, і після Ви закінчили, зовнішня посилання все одно вказуватиме на початковий об'єкт.
Якщо ви передаєте незмінний об'єкт методу, ви все одно не можете відновити зовнішню посилання, і ви навіть не можете мутувати об'єкт.
Щоб зробити це ще більш зрозумілим, давайте кілька прикладів.
Список - змінний тип
Спробуємо змінити список, переданий методу:
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)
Вихід:
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
Оскільки переданий параметр є посиланням на нього outer_list
, а не на його копію, ми можемо використовувати методи мутації списку, щоб змінити його і відобразити зміни у зовнішній області.
Тепер давайте подивимося, що станеться, коли ми спробуємо змінити посилання, яке було передано як параметр:
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)
Вихід:
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']
Оскільки the_list
параметр був переданий за значенням, присвоєння йому нового списку не вплинуло на те, що код поза методом не міг бачити. Це the_list
була копія outer_list
посилання, і ми the_list
вказували на новий список, але змінити там, де outer_list
вказано, не було.
Рядок - непорушний тип
Це незмінне, тому ми нічого не можемо зробити, щоб змінити вміст рядка
Тепер спробуємо змінити посилання
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)
Вихід:
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
Знову ж таки, оскільки the_string
параметр був переданий за значенням, присвоєння йому нового рядка не вплинуло на те, що код поза методом не міг бачити. Це the_string
була копія outer_string
посилання, і ми the_string
вказували на новий рядок, але змінити місце, де outer_string
вказано, не було.
Я сподіваюся, що це трохи прояснить справи.
EDIT: Помічено, що це не відповідає на питання, яке @David спочатку запитував: "Чи можна щось зробити, щоб передати змінну за фактичною посиланням?". Давайте попрацюємо над цим.
Як ми можемо обійти це?
Як показує відповідь @ Андреа, ви можете повернути нове значення. Це не змінює спосіб передачі речей, але дозволяє отримувати потрібну інформацію:
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)
Якщо ви дійсно хотіли уникати використання повернутого значення, ви можете створити клас, щоб утримувати ваше значення і передавати його у функцію, або використовувати існуючий клас, наприклад, список:
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])
Хоча це здається трохи громіздким.