Почему аргумент List в Python ведет себя как ByRef? - PullRequest
1 голос
/ 09 октября 2009

Это может быть для большинства языков в целом, но я не уверен. Я новичок в Python и всегда работал над копиями списков в C # и VB. Но в Python всякий раз, когда я передаю список в качестве аргумента и перечисляю с помощью «for i in range», а затем меняю значение аргумента списка, входные значения фактически изменяют исходный список. Я думал, что Python должен был передавать аргументы по значению по умолчанию, так что после завершения функции у меня все еще будут исходные значения до того, как я вызвал функцию. Что мне не хватает? Спасибо!

Ответы [ 3 ]

8 голосов
/ 09 октября 2009

Python передает аргументы по значению, но полученное вами значение является копией ссылки (кстати, это точно так же, как ведут себя C #, VB.NET и Java).

Это важно запомнить:

Объекты не передаются по ссылке - ссылки на объекты передаются по значению .

Поскольку у вас есть копия ссылки, любая операция, на которую указывает эта ссылка, будет такой же, как если бы вы держали саму исходную ссылку.

4 голосов
/ 09 октября 2009

Python - точно так же, как Java делает для чего угодно, кроме примитивных скаляров, и как C # и VB.NET делают для параметров вида по умолчанию в отличие от штучных типов и параметров out / ref - передает «по ссылке на объект» (поиск эта фраза здесь - это то, что Гвидо, архитектор и создатель Python, использует для объяснения этой концепции передачи аргументов).

Каждое имя является ссылкой на некоторый объект; передача имени (или любого другого выражения) в качестве аргумента просто создает еще одну ссылку на тот же объект (к которому тело функции может получить доступ через имя параметра). ((Нет такой вещи как «ссылка на имя»: есть имена , которые являются одним из видов ссылки на объекты, а объект - точка)).

Когда вы передаете изменяемый объект, то есть тот, у которого есть изменяющие методы (например, список), вызываемая функция может изменять объект, вызывая, прямо или косвенно, его изменяющие методы. ((Под «косвенно» я имею в виду «через операторов» - например:

somelist[len(somelist):] = [whatever]

в точности совпадает с somelist.append(whatever).))

Если вы хотите передать список в функцию, но не хотите, чтобы функция могла каким-либо образом изменять этот список, вы должны передать копию список вместо оригинала - как в Java, C #, VB.NET.

Будьте очень ясно о различии между повторным связыванием имени и , мутировавшего с объектом . Перепривязка имени («barename», то есть - квалифицированные имена отличаются ;-) только влияет на то, что name - NOT любой объект вообще. Например:

def f1(alist):
  alist = [23]

def f2(alist):
  alist[:] = [23]

Можете ли вы определить разницу между этими двумя функциями? Один из них - это повторное связывание barename alist - без какого-либо влияния на что-либо. Другой - мутирование (изменение, изменение, ...) объекта списка, который он получил в качестве аргумента, - путем установки его содержимого в виде списка из одного элемента с int как единственным элементом. Совершенно, совершенно разные вещи !!!

0 голосов
/ 09 октября 2009

Чтобы добавить к ответу Эндрю, вам нужно явно сделать копию списка, если вы хотите сохранить оригинал. Вы можете сделать это, используя модуль copy, или просто сделать что-то вроде

a = [1,2]
b = list(a)

Поскольку копирование объектов обычно подразумевает снижение производительности, я считаю полезным явно использовать модуль копирования в моих крупных проектах. Таким образом, я могу легко найти все места, где я собираюсь использовать гораздо больше памяти.

...