Python 2.6 автоматически превращает переменные в определении функции в глобальные при определенных обстоятельствах? Почему?

#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: Вы правы, я думаю, что сначала я нажал «Опубликовать ответ» только с первых двух строк. Затем добавил две (больше) строки.