#python #debugging #global-variables #issue-tracking #local-variables
#python #отладка #глобальные переменные #отслеживание проблем #локальные переменные
Вопрос:
Я абсолютно ошарашен тем, почему происходит следующее:
Вот мой код:
def add_one(array):
new_array = array
length = len(array)
for i in range(length):
new_array[i] = new_array[i] 1
return new_array
x = [1,2,3,4,5];
y = add_one(x)
print x
print y
Вот результаты:
[2, 3, 4, 5, 6]
[2, 3, 4, 5, 6]
Я не понимаю, почему x изменен.
Мои предположения: Каким-то образом x задается как глобальная переменная для функции add_one. Я включил ‘new_array = array’, чтобы, если array каким-то образом был глобальной переменной x, x не был бы изменен. Однако каким-то образом new_array также стал глобальной переменной x при выполнении ‘new_array = array’. Я написал альтернативную версию функции add_one, которая не вызвала у меня проблем:
def add_one(array):
new_array = []
length = len(array)
for i in range(length):
new_array.append(array[i] 1)
Кажется, что если локальную переменную (то есть массив) редактировать по ее индексу внутри функции, она становится глобальной по отношению к глобальной переменной, которая была принята в качестве входных данных для этой функции?
Я понятия не имею, что происходит. Любые объяснения были бы высоко оценены.
Комментарии:
1. Определение области видимости в Python иногда не так чисто, как некоторые могли бы ожидать, но это не так странно 😉
Ответ №1:
Когда вы говорите new_array = array
, вы не создаете копию массива, вы просто создаете другое имя для массива. Оба имени по-прежнему применяются к одному и тому же массиву.
Чтобы сделать копию, самый простой способ — использовать нарезку: new_array = array[:]
Комментарии:
1. Это отлично работает. Однако, почему array вообще рассматривается как глобальная переменная в первую очередь?
2. @John, это не глобальное. Вы передали это в функцию, и функция изменила это. Вы не думали , что изменяете его, потому что используете другое имя.
Ответ №2:
Ничто не становится глобальным для неясных операций чтения. array
по-прежнему является совершенно обычной локальной переменной. Но это ссылка, поскольку все в Python (ну, по крайней мере, каждое имя) является ссылкой:
add_one(x)
передает ссылку (не как ссылку, например, в C , скорее как указатель — если что-то из этого для вас что-то значит) вx
вadd_one
new_array = array
просто скопирует эту ссылку- Никакого копирования не происходит, все эти имена указывают на один и тот же объект!
Это означает, что вызов метода в array
in add_one
модифицировал тот же самый объект, на который ссылается вызывающий — повторное присвоение x
( array
) просто перезапишет локальную копию ссылки и не повлияет на другие переменные, ссылающиеся на тот же объект (в конце концов, вполне возможно, что такой переменной нет!), Но назначение члена или элемента (например, array = []
) в основном является вызовом метода и new_array[i] = ...
будет изменять объект. ……………. ()
Как правило, самый простой способ сгенерировать новый список из существующего с некоторым шаблоном модификации, применяемым к каждому элементу, и, при необходимости, некоторой фильтрацией — использовать понимание списка. [x 1 for x array]
в этом примере.
Комментарии:
1. Спасибо! Это полностью прояснило мое замешательство.
2. Я понял, что вы сказали, но обрабатываются ли массивы по-разному? Например:
3. def add(num): num[0]=num[0] 1 x=[1] add(x); вывести x
4. def add(num): num=num 1 x=1 add(x); вывести x
5. в двух приведенных выше фрагментах кода один изменяет значение x, а другой — нет. (Когда x является массивом, он изменяется).
Ответ №3:
В третьей строке вы устанавливаете y в x .
new_array = array
Ответ №4:
Эта линия является решающей:
new_array = array
Вы должны видеть new_array
не как новую переменную, а как новое имя для переменной (объекта), у которой уже есть имя array
.
Комментарии:
1. @delnan: Не уверен, почему ты так говоришь. Мой ответ не такой подробный, как ваш, но то, что
new_array
является просто «именем» или «ссылкой» на один и тот же объект, имеет решающее значение, не так ли?2. @delnan: Я не вижу никаких причин для того, чтобы отклонять этот ответ. Это коротко, достоверно и корректно. У кого-то может не быть мотивов голосовать за это, но в этом нет ничего плохого.
3. @jsbueno: Особенно оригинальная версия, не показанная в истории редактирования (очень быстрые изменения не записываются), в основном просто говорит «эта строка вызывает эту проблему», даже не намекая на какие-либо знания, необходимые для понимания причины. Эта версия немного лучше, но я думаю, все еще очевидно, что OP потребовал дополнительного объяснения, почему это важно.
4. @delnan: Вы правы, я думаю, что сначала я нажал «Опубликовать ответ» только с первых двух строк. Затем добавил две (больше) строки.