Страницы

Поиск по вопросам

четверг, 13 февраля 2020 г.

Обращение к переменной по ссылке в python

#python #python_3x #указатели #ссылка


Сегодня буду краток. Есть код:

a, b, c = 1, 2, 3
arr = [a, b, c]
for i in range(3):
    arr[i] += 1

print(a, b, c)  # output: 1 2 3


А мне нужно output: 2 3 4. Как можно, изменяя элементы в списке, изменить элементы,
которые были добавлены изначально? Что-то вроде ссылки и указателя?

UPD: я значительно упростил код, поэтому дам некоторые уточнения: так называемые
переменные a, b и c являются атрибутами класса, и я сомневаюсь, что можно решить проблему
через globals()
    


Ответы

Ответ 1



Выражение arr[i] += 1 соответствует: arr[i] = arr[i] + 1 что переписывает ссылку, на новую (с новым значением), поэтому ваш код и не работает. Вообще a, b, c = 1, 2, 3 означает, что переменные a, b, c хранят ссылки на объекты 1, 2, 3. А тут arr = [a, b, c] вы не ссылки на переменные положили, а сами ссылки на объекты 1, 2, 3. Вам проще к самим переменным обратиться и поменять значением, чем так. Либо оставить значения в словаре или в списке и работать с ними. Но если очень хочется, можно рефлексией поменять у переменных: a, b, c = 1, 2, 3 arr = [a, b, c] for i in ['a', 'b', 'c']: globals()[i] += 1 print(a, b, c) # 2 3 4 и я сомневаюсь, что можно решить проблему через globals() Через globals() и вправду не получится, т.к. оно меняет глобальные переменные, а вот для изменения в объектах использовать getattr и setattr: class Foo: a, b, c = 1, 2, 3 foo = Foo() arr = [foo.a, foo.b, foo.c] for i in ['a', 'b', 'c']: x = getattr(foo, i) setattr(foo, i, x + 1) print(foo.a, foo.b, foo.c) # 2 3 4

Ответ 2



Kогда мне нужно обеспечить изменение объекта "по ссылке", я применяю такой способ: a, b, c = [1], [2], [3] arr = [a, b, c] for i in range(3): arr[i][0] += 1 print(a[0], b[0], c[0]) # output: 2 3 4 Объяснение. Немного общей теории: Объекты числового типа неизменяемы, к такому объекту нельзя прибавить единицу, можно только создать новый числовой объект с нужным значением и заменить на него исходный. Соответственно: а = 1 означает: создай объект со значением 1 и помести его в глобальный словарь с переменными (globals) под именем a. а = a + 1 означает: найди в словаре с переменными объект под именем a, сложи его с объектом 1, результат запиши в новый объект 2. В глобальном словаре замени значение a ссылкой на этот объект. a, b, c = 1, 2, 3 - создай три числовых объекта, запиши их ссылки в словарь с переменными под именами a, b, c, соответственно. Тут ещё задействованы кортежи, но про них не буду говорить, чтобы не усложнять. Объекты типа list изменяемы, в список можно добавлять и удалять значения, можно менять уже находящиеся в списке. Список хранит не сами объекты, а ссылки на них, меняя значение в списке, мы на самом деле заменяем ссылку предыдущего объекта на новую, указывающую на другой объект. arr = [a, b, c] - создай объект типа список и наполни его ссылками на объекты, которые хранятся в словаре с переменными под именами a, b, c. Какая ссылка хранится под именем a? Ссылка на числовой объект 1. Под именем b ссылка на 2 и т. д. Таким образом, имеем arr = [1, 2, 3] - список arr не хранит ни самих переменных, ни информации из каких переменных объекты 1, 2, 3 были получены. Кстати, теперь каждый из трёх объектов 1, 2, 3 имеет на себя как минимум по две ссылки - одна в глобальном словаре с переменными (a, b, c), другая внутри списка arr. Когда вы делаете arr[0] += 1, вы берёте из списка нулевой элемент, который представляет из себя ссылку на числовой объект 1 и прибавляете к этому объекту единицу. Информации про переменную, с помощью которой вы этот объект назначали, в списке нет. Результат (ссылку на новый объект) записываете обратно в нулевой элемент списка. Использование списка для сохраненения ссылки на значение переменной: a, b, c = [1], [2], [3] - оборачиваем каждый числовой объект в список и пишем ссылку на этот список в переменную. Помним, что объект типа список хранит не сам числовой объект, а ссылку на него. Следовательно, под именем a у нас хранится ссылка на список, который содержит единственный элемент - ссылку на объект 1. arr = [a, b, c] - эту строчку можно представить как arr = [[1], [2], [3]]. Информацию о переменных a, b, c мы потеряли также, как и в предыдущем случае, однако сейчас и переменная a, и arr[0] указывают на список [1]. И этот список содержит нужное нам значение, которое мы можем менять, сохраняя в a и arr[0] ссылку на список-обёртку. arr[0][0] += 1 - возьми нулевой элемент из списка. В нашем случае это [1]. Возьми и у него нулевой элемент. Это будет числовой объект 1. Прибавь к нему единицу. Получившимся новым объектом замени нулевой элемент в списке [1]. Список мы не трогаем, а лишь меняем внутри него элемент на другой. Ссылка на список остаётся неизменной, переменная a продолжает на него указывать, только теперь этот список содержит 2 вместо 1. Мой ответ на похожую тему. В мануале на английском этот способ под № 3: How do I write a function with output parameters (call by reference)?.

Ответ 3



a, b, c = 1, 2, 3 arr = [a, b, c] a, b, c = map(lambda x: x+1, arr) print(a, b, c) 2 3 4

Комментариев нет:

Отправить комментарий