Как передать по ссылке python
Перейти к содержимому

Как передать по ссылке python

  • автор:

Как передать переменную по ссылке в Python?

В Питоне аргументы функций передаются путем присваивания. Поскольку присваивание просто создает новые ссылки на объекты, то между именами аргумента в вызывающем и вызываемом методе нет общей ссылки.

Как же передать аргумент по ссылке? Вы можете добиться нужного эффекта несколькими способами.

Возвращение кортежа

def my_func(x, y): # x и y локальные имена, которым присвоены новые объекты x = 'новое значение' y = y + 1 # возвращаем новые значения return x, y a, b = 'старое значение', 9 a, b = my_func(a, b) print(a, b) результат: новое значение 10

Это почти всегда самое ясное решение.

Использование глобальных переменных

Использование глобальных переменных не рекомендуется, т. к. не потокобезопасно.

Передача изменяемого (mutable) объекта

def func1(a): # 'a' ссылается на изменяемый список a[0] = 'новое значение' a[1] = a[1] + 1 args = ['старое значение', 9] func1(args) print(args) результат: ['новое значение', 10]

Передача словаря

def func2(args): # args - изменяемый словарь args['a'] = 'новое значение' args['b'] = args['b'] + 1 args = func2(args) print(args) результат:

Передача экземпляра класса

class MyNamespace: def __init__(self, /, **args): for key, value in args.items(): setattr(self, key, value) def func3(args): args.a = 'новое значение' args.b = args.b + 1 args = Namespace(a='старое значение', b=9) func3(args) vars(args) результат:

Почти никогда не бывает веской причины, чтобы все так усложнять.

Лучший вариант из представленных – это вернуть кортеж, содержащий несколько возвращаемых значений.

В Питоне переменные передаются по ссылке или по значению? Есть подводные камни?

Неизменяемые объекты передаются по значению. Это значит, что при изменении значения переменной будет создан новый объект. К этому типу относятся:

  • числовые данные (int, float, complex)
  • символьные строки (str)
  • кортежи (tuple) При инициализации переменной незменяемого типа создается объект (например, целое число), этот объект имеет некоторый идентификатор:
>>> a = 10 >>> id(a) 10914656 

оператор = связывает переменную a и объект посредством ссылки. При этом вы не можете изменить сам объект, т.е. когда вы присвоите переменной новое значение, интерпретатор создаст новый объект (если до этого этот объект был создан, то переменная просто получит ссылку), а первоначальный объект удалится из памяти сбощиком мусора, если ссылок на него больше нет.

Изменяемые (mutable)

Изменяемые объекты передаются по ссылке. Это значит, что при изменении значения переменной объект будет изменен. К этому типу относятся:

  • списки (list)
  • множества (set)
  • словари (dict)

Подводные камни

Создадим список a , установим для переменной b ссылку на a , прибавим к b элемент списка и выведем их значения и идентификаторы на экран:

>>> a = [1, 2] >>> b = a >>> b.append(3) >>> print(a, b) [1, 2, 3] [1, 2, 3] >>> print(id(a), id(b)) 139748057891656 139748057891656 

Как мы видим, переменные имеют одинаковые id и элементы списка. Если ты не знаешь об этой особенности изменяемых объетов, то такое поведение программы для тебя становится полной неожиданностью и может привести к ошибке в работе программы. Таким же образом с помощью ссылки на изменяемый объект, переменная передается в функцию:

>>> def add_value(a): . a.append(3) >>> b = [1, 2] >>> add_value(b) >>> print(b) [1, 2, 3] 

Даже возвращая None , функция изменила список b , чего бы нам не хотелось.

Что с этим можно сделать

Для того, чтобы передать в функцию изменяемую переменную как значение, нужно сделать копию изменяемого элемента. Создадим копию списка:

новый_лист = старый_лист[:] 

Тоже самое можем сделать вот так:

новый_лист = list(старый_лист) 

Переменная новый_лист ссылается на новый объект:

>>> id(старый_лист), id(новый_лист) (139748050279112, 139748057891656) 

Это дает нам возможность изменять оба объекта независимо друг от друга.

Что почитать

  • Переменные-ссылки в Python
  • Understanding Python variables and Memory Management

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.

Язык C++

Давайте напишем функцию add_item , которая принимает контейнер vector и добавляет в конец контейнера новый элемент. Мы могли бы начать со следующего кода:

#include #include #include using namespace std; // Здесь есть проблема void add_item(vectorstring> vec)  vec.push_back("New item!"); > int main()  vectorstring> vec; add_item(vec); for (string s : vec)  cout  <s  <'\n'; > return 0; > 

Если мы скомпилируем и запустим эту программу, то обнаружим, что после вызова функции add_item контейнер vec остался пустым. Проблема в том, что мы передали в функцию копию объекта vec . Внутри функции к этой копии был добавлен новый элемент, а после выхода из функции копия была удалена.

Следующий вариант нашей программы уже будет делать то что мы хотим:

// Здесь есть проблема vectorstring> add_item(vectorstring> vec)  vec.push_back("New item!"); return vec; > int main()  vectorstring> vec; vectorstring> vec2 = add_item(vec); for (string s : vec2)  cout  <s  <'\n'; > return 0; > 

Такая реализация, однако, является очень плохой идеей. Мы всего лишь хотели добавить один элемент в вектор, а вместо этого получили копию вектора с добавленным новым элементом. Помимо неверной логики работы, мы получили потенциальную проблему с производительностью: вместо константного времени мы тратим линейное время на добавление элемента в вектор.

Правильное решение нашей задачи в C++ выглядит следующим образом:

void add_item(vectorstring>& vec)  vec.push_back("New item!"); > int main()  vectorstring> vec; add_item(vec); for (string s : vec)  cout  <s  <'\n'; > return 0; > 

Символ амперсанд & позволяет передать в функцию ссылку на параметр. Работа с параметром внутри функции не изменяется, но вместо копии мы имеем дело именно с тем объектом, который был передан в функцию. Таким образом, мы избавились от лишнего копирования и реализовали правильную логику работы программы.

Рассмотрим другой пример. Допустим, мы хотим передать вектор в функцию, которая будет анализировать элементы вектора, но не будет его изменять. Например:

// Здесь есть проблема int count_greetings(vectorstring>& vec)  int counter = 0; for (string s : vec)  if (s == "Hello")  ++counter; > > return counter; > 

Мы уже достаточно грамотные и сразу передали вектор по ссылке, чтобы избежать ненужного копирования. Однако в текущем виде функция count_greetings имеет другую, более тонкую, проблему. Если мы нарушим договоренность и изменим вектор внутри функции count_greetings , то компилятор не увидит в этом проблемы. Проблему будем искать мы, когда поймем, что в каком-то месте нашей программы происходит неправильная манипуляция с вектором.

Хорошим стилем в данном случае является передача параметра по константной ссылке:

int count_greetings(const vectorstring>& vec)  int counter = 0; for (const string& s : vec)  if (s == "Hello")  ++counter; > > return counter; > 

Теперь компилятор не позволит изменить объект vec внутри функции count_greetings . Кроме того, теперь в коде явно выражена мысль о том, что объект передается в функцию только для чтения. Такой код проще читать и понимать логику его работы. Обратите внимание, что мы воспользовались константной ссылкой при определении переменной в цикле for . Здесь мы имеем дело с аналогичной ситуацией: в предыдущей версии в переменную s по очереди копировался каждый элемент вектора. Теперь же мы перебираем в цикле константные ссылки на объекты, не копируя их.

Иногда необходимо изменять элементы вектора в цикле. В таком случае необходимо использовать неконстантную ссылку:

for (string& s : vec)  s.push_back('!'); > 

Передача константной ссылки на объект в функцию, которая не имеет право изменять объект, имеет смысл только в том случае, если копирование объекта является дорогой операцией. В частности, нет никакого смысла в передаче по ссылке объектов int или double . Это наоборот может привести к потере производительности. Если же мы имеем дело со сложным объектом, таким как string или любым контейнером, то передача по константой ссылке является единственным верным решением.

Использование ссылок в C++ не ограничивается передачей параметров в функции, но с этого примера проще всего начать знакомство со ссылками. Ключевое слово const также имеет разнообразные применения в C++. О некоторых из них мы поговорим в дальнейшем.

Резюме

Мы обсудили три способа передачи параметров в функцию:

  • передача копии
  • передача по ссылке
  • передача по константной ссылке

Передавать копию объекта имеет смысл, если копирование стоит дешево, либо когда того требует логика программы. Передача по ссылке используется, если необходимо модифицировать передаваемый объект. Передача по константной ссылке позволяет избежать копирования больших объектов.

Источники

  • isocpp.org/wiki/faq/references
  • Введение
  • Настройка рабочей среды
  • Язык C++
    • Работа с потоками ввода-вывода
    • Строки
    • Контейнеры стандартной библиотеки C++
    • Эффективная передача параметров в функцию
    • Алгоритмы стандартной библиотеки C++
    • Итераторы
    • Библиотеки numeric и random
    • Классы
    • Наследование
    • Динамическое выделение памяти
    • Обобщенное программирование

    Как передать в функцию переменную по ссылке? Python

    Хочу сделать что-то вроде этого только для переменной, не списка:

    def fn(a: list): a[0] += 1 b = [2] fn(b) print(b) # будет выведенно 3, как я и хочу. 

    Я хочу код на python похожий на этот, написанный на C++17:

    #include using namespace std; void fn(int &a) < ++a; >int main()

    Отслеживать

    19.7k 6 6 золотых знаков 22 22 серебряных знака 56 56 бронзовых знаков

    задан 11 мая 2020 в 13:53

    587 3 3 серебряных знака 20 20 бронзовых знаков

    1 ответ 1

    Сортировка: Сброс на вариант по умолчанию

    def user_sum(a: int, b: int): result = a + b return result a = 2 result = user_sum(a, 2) print(result) 

    Отслеживать

    ответ дан 11 мая 2020 в 14:16

    3,102 2 2 золотых знака 15 15 серебряных знаков 22 22 бронзовых знака

    Вам стоит почитать про типы данных list и int и что с ними можно делать

    11 мая 2020 в 14:28

    посмотрите на код, написанный на C++, который я добавил.

    11 мая 2020 в 15:02

    @llollcat в Python нет ссылок на переменные. Так как даже целочисленные переменные там неизменны. При изменении переменной — она не меняется, а создаётся новая в памяти. По этой причине указатели бессмысленны.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *