Аргументы передаются по назначению . Обоснование этого двоякое:
- переданный параметр на самом деле является ссылкой на объект (но ссылка передается по значению)
- некоторые типы данных являются изменяемыми, а другие нет
Так:
Если вы передадите изменяемый объект в метод, метод получит ссылку на этот же объект, и вы сможете изменить его на радость своего сердца, но если вы перепривязаете ссылку в методе, внешняя область ничего не будет знать об этом, и после все готово, внешняя ссылка все еще будет указывать на исходный объект.
Если вы передадите неизменный объект методу, вы все равно не сможете перепривязать внешнюю ссылку и даже не можете изменить объект.
Чтобы было еще яснее, давайте приведем несколько примеров.
Список - изменяемый тип
Давайте попробуем изменить список, который был передан методу:
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
указано.
Надеюсь, это немного прояснит ситуацию.
РЕДАКТИРОВАТЬ: Было отмечено, что это не отвечает на вопрос, который первоначально задавал @David, «Есть ли что-то, что я могу сделать, чтобы передать переменную по фактической ссылке?». Давайте работать над этим.
Как мы можем обойти это?
Как показывает ответ @ Andrea, вы можете вернуть новое значение. Это не меняет способ передачи данных, но позволяет получить информацию, которую вы хотите вернуть:
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])
Хотя это кажется немного громоздким.