Пример:
def exampol(x, y):
for i in x:
x[i] = x[i]*x[i]
for j in range(0, len(y)):
y[j] = y[j]*y[j]
return x, y
x = [0,1,2,3,4]
y = [0,10,20,30,40,50,60]
exampol(x, y)
print(x)
print(y)
После вызова функции exampol()
вывод:
x = [0, 1, 4, 9, 16]
y = [0, 100, 400, 900, 1600, 2500, 3600]
Я не понимаю, почему это происходит. Насколько я знаю, переменные в функции являются локальными по области действия, и параметры, передаваемые в функцию, не должны изменяться по сравнению с их исходным значением.
Почему исходные переменные передаются в качестве аргументов измененной функции?
Это два разных понятия: одно — имя или идентификатор (то, что мы называем «переменной»), а другое — объект, с которым оно связано. Ваш список не определен внутри функции, вы передаете его в качестве аргумента.
В Python переменная — это просто имя, идентификатор, связанный со ссылкой на объект в памяти и используемый для доступа к нему. Один и тот же объект может иметь несколько связанных с ним имен:
Если мы делаем:
a
иb
это имена, которые связаны с одним и тем же объектомlist
в памяти , мы можем сделать ихa
связанными с другим объектом, переназначив:Мы можем использовать
a
или любое другое имя, связанное с объектом, для доступа к нему, его методам и атрибутам:Переменная, определенная внутри функции, существует только внутри этой функции, как вы комментируете, также любая попытка переназначения глобальной переменной внутри функции приводит к созданию локальной переменной с тем же именем, оставляя глобальную нетронутой (пока мы не использовать
global
илиnonlocal
). Можно сказать, что глобальные идентификаторы внутри функции по умолчанию доступны только для чтения, потому что интерпретатор сначала ищет переменную в локальном для функции пространстве имен, а если не находит, то ищет в глобальном.Теперь любая попытка изменить объект, к которому присоединена глобальная переменная, внутри функции автоматически создает локальную переменную с тем же именем. Когда я говорю «изменить объект, с которым он связан», я не имею в виду изменение свойств самого объекта, я имею в виду, что переменная указывает на другой объект, присваивание:
Аргументы функции/метода в Python передаются по назначению, по «ссылке на объект» (вы не передаете значение или копию объекта, вы просто передаете ссылку на объект в памяти).
В дополнение к передаче по ссылке на объект важно учитывать концепцию изменчивости объекта (*см. список в конце ответа). Списки являются изменяемыми объектами , это означает, что списки, которые вы изменяете внутри функции, являются теми же объектами, что и те, которые вы передаете ей в качестве аргумента:
Если объект является неизменяемым, например целым числом, числом с плавающей запятой или строкой, этого не происходит, потому что «изменение» неизменяемого объекта с помощью переменной на самом деле всегда означает создание нового объекта и присвоение новой ссылки переменной. Поэтому, когда мы делаем что-то вроде внутри функции
x += 1
, будучиx
целым числом, переданным в качестве аргумента, имяx
становится связанным с новым объектом в памяти, поскольку последовательность состоит в том, чтобы добавить единицу к x, сохранить его в новом целом объекте и связать ( присвоить) ему имяx
:Вылет из:
Поскольку список является изменяемым объектом, изменение его значений внутри функции не создает новый объект, а изменяется напрямую:
Вылет из:
Если вы не хотите, чтобы это произошло, вы должны либо передать копию списка функции, либо создать копию внутри самой функции:
В этом случае копия создается путем нарезки, также можно использовать
copy.copy()
. Если ваш список содержит только целые числа (неизменяемые), этого достаточно. Если в вашем списке есть изменяемые элементы, такие как списки или словари, приведенное выше просто создает копию списка, но его элементы остаются такими же, как исходный список (те же ссылки), поэтому вы должны использоватьcopy.deepcopy()
так, чтобы содержащиеся в нем элементы также копировались.Объекты Python в соответствии с их изменчивостью:
Чтобы завершить ответ, принимая во внимание комментарий @fedorqui, я оставляю классификацию объектов в стандартном Python в соответствии с их изменчивостью:
Изменяемый:
bytearray
dict
list
set
Неизменный:
bool
bytes
complex
decimal
int
float
frozenset
str
/unicode
tuple
range
В дополнение к вышесказанному, в стандартной библиотеке у нас есть модуль
collections
, который предоставляет альтернативы предварительно созданным контейнерам и обычно является оболочкой или основан на предыдущих (и, следовательно, имеет такое же свойство неизменяемости, как эти).