Поведение глобальных переменных
Данный код не изменит глобальные значения переменных x и y:
def abc(x, y):
x = x + [4]
y += 'nnn'
return print(x, y)
x = [1, 2, 3]
y = 'abc'
abc(x, y)
print(x, y)
вывод программы:
[1, 2, 3, 4] abcnnn
[1, 2, 3] abc
Но если изменить оператор в выражении x = x + [4] на x += [4], то список изменится для глобального имени x.
Вывод будет:
[1, 2, 3, 4] abcnnn
[1, 2, 3, 4] abc
Почему так происходит? То есть неизменяемая последовательность не изменилась, а изменяемый список все же изменился без использования слова global в области функции. Я понимаю, что такие вещи как x.append() поменяли бы глобальный x, но речь вроде идет о присваивании. А точнее о виде присваивания, что такого в операторе +=, что он позволяет менять глобальный x ?
Ответы (3 шт):
def abc(x, y):
x = x + [4]
y += 'nnn'
return print(x, y)
Тут x - это локальная переменная, никак не связанная с глобальной переменной с тем же именем. Но список, переданный через этот параметр - связан (он же хранится в глобальной переменной).
Когда вы делаете присваивание
x = x + [4]
- вы просто записываете в локальную переменную новое значение (новый список). Значение в глобальной переменной при этом никак не изменяется.
Когда вы делаете
x += [4]
- вы не заменяете значение в переменной на новый список, а мутируете значение (добавляете элементы в исходный список). Т.к. это же значение (список) хранится и в глобальной переменной, то это изменение отображается и в ней.
И нет, x = x + ... не эквивалентно x += .... В первом случае создается новый список, результат записывается в x, во втором случае изменяется исходный список.
Если бы оператор + не создавал новый список, а модифицировал левый список, то вот такой код:
a = [1]
b = [2]
print(a + b)
print(a + b)
print(a + b)
print(a + b)
при каждом print выдавал бы новый результат (т.к. список a изменился), но он выдает один и тот же результат (как и ожидаешь интуитивно).
Хотя, конечно, ничего не мешает написать такой класс, в котором вызов __add__ модифицирует объект, а не создает новый, но это будет очень неинтуитивно.
+= - это не обязательно просто короткая запись для сложения с последующим присваиванием.
Вы можете для любого своего класса задать метод __iadd__, благодаря чему при += может быть выполнена вообще произвольная логика.
В частности для стандартного списка += работает как метод list.extend.
Поэтому он не пересоздаёт переменную с тем же именем, а изменяет существующую.
def abc(x):
x += [4]
print(f"2 x = {x} id(x) = {id(x)}")
if __name__ == '__main__':
x = [1, 2, 3]
print(f"1 x = {x} id(x) = {id(x)}")
abc(x)
print(f"3 x = {x} id(x) = {id(x)}")
Результат работы:
1 x = [1, 2, 3] id(x) = 2421664311424
2 x = [1, 2, 3, 4] id(x) = 2421664311424
3 x = [1, 2, 3, 4] id(x) = 2421664311424
Это один и тот же объект id = 2421664311424