Передача переменной в функцию по "ссылке"
Начинаю знакомиться с питоном.
Насколько я понял из гугла, передача переменной из функции в функцию осуществляется путем копирования ссылки на эту переменную. Получается, что сами данные из переменной не копируются и это позволяет быстро "передавать" данные из одной функции в другую не важно какого размера эта переменная так?
Теперь я пытаюсь это проверить. Я в PyCharm пишу:
Не компилируется листинг "Передача по ссылке аргументов" Р.Лафоре
Вообщем в 5 Главе имеется листинг. Набрал его в Visual Studio 2013. При компиляции выдает ошибку.
Передача параметра в ссылке без знака "?"
Здравствуйте! Обычно передача параметра в ссылке выглядит так: domen.ru/?id=1 Скажите.
Выдает ошибку "использована неинициализированная локальная переменная "flag" " и с переменной "sum_check" та же проблема
//func.cpp #include<iostream> using namespace std; #include "func.h" //Функция, которая.
ildwine, спасибо, я проглядел про return
Получается вышеизложенное мое понимание о передаче переменной ошибочно?
Сообщение было отмечено Kerim_Geophysic как решение
Решение
Сообщение от Kerim_Geophysic
Ты не меняешь список — ты создаешь новую переменную и присваиваешь ей число.
Изменение по ссылке (а в Python все по ссылке) работает по другому. Ты можешь изменять список, то есть менять внутри него значения.
Сообщение от Kerim_Geophysic
Сообщение от ildwine
Не совсем так. В Python механизм называется Call By Object Reference. И так как в Python все есть объект — все передается по ссылке (даже числа и строки, что легко проверить через id). Но при передаче неизменяемых объектов передача действует как вызов по значению, неизменяемых — как вызов по ссылке.
Добавлено через 43 секунды
Сообщение от Kerim_Geophysic
А здесь совсем другая переменная val, которой в заголовке функции идет не явное присвоение ссылки на объект 1, а внутри функции в нее помещается ссылка уже на объект 4. Но эта переменная val никак не влияет на переменную val, которая снаружи. Поэтому переменная val снаружи функции продолжает ссылаться на объект 1.
Информацию об областях переменных Вы можете почитать в той же книге, в главе 17. "Области видимости".
Добавлено через 13 минут
Kerim_Geophysic, В принципе Garry Galler, Вам уже объяснил, я просто дополнил, где еще Вы можете об этом почитать.
Передача по ссылке в Python
В этом руководстве мы продемонстрируем, что вам нужно знать о передаче по ссылке. Ниже мы включили образец программы, следуя которой вы сможете лучше понять эту функцию.
Определение передачи по ссылке в функции Python
Есть много способов определить аргумент в функции в Python; один из этих процессов — передача по ссылке. Слово передать здесь означает передать или передать аргумент функции. Тогда по ссылке означает, что аргумент, переданный функции, в основном упоминается как существующая переменная, а не как отдельная копия этой переменной. В этом методе определения аргумента в функции на переменную, на которую была сделана ссылка, в основном влияет любая выполняемая операция.
Пример передачи по ссылке в Python
Объяснение
В приведенном выше примере функция сначала определяется переменной x . Здесь метод append используется с x для добавления к имени элемента sam . После этого составляется список с использованием элемента x , в котором только один элемент, то есть hello . После печати списка функция, которая была определена изначально, вызывается вместе со своим аргументом x . После вызова функции обратите внимание, что добавленный элемент в самой функции был добавлен в список x .
Этот процесс показывает, как работает передача по ссылке . Функция всегда влияет на изменяемые объекты (объекты, которые могут изменять свое значение или состояние), хранящиеся в переменной, используемой в качестве аргумента функции.
Обращение к переменной по ссылке в python
А мне нужно output: 2 3 4 . Как можно, изменяя элементы в списке, изменить элементы, которые были добавлены изначально? Что-то вроде ссылки и указателя?
UPD: я значительно упростил код, поэтому дам некоторые уточнения: так называемые переменные a , b и c являются атрибутами класса, и я сомневаюсь, что можно решить проблему через globals()
Kогда мне нужно обеспечить изменение объекта «по ссылке», я применяю такой способ:
Объяснение.
Немного общей теории:
Объекты числового типа неизменяемы, к такому объекту нельзя прибавить единицу, можно только создать новый числовой объект с нужным значением и заменить на него исходный. Соответственно:
а = 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] ссылку на список-обёртку.
Указатели в Python: в чём суть?
Если вы когда-нибудь работали с такими низкоуровневыми языками, как С или С++, то наверняка слышали про указатели. Они позволяют сильно повышать эффективность разных кусков кода. Но также они могут запутывать новичков — и даже опытных разработчиков — и приводить к багам управления памятью. А есть ли указатели в Python, можно их как-то эмулировать?
Указатели широко применяются в С и С++. По сути, это переменные, которые содержат адреса памяти, по которым находятся другие переменные. Чтобы освежить знания об указателях, почитайте этот обзор.
Благодаря этой статье вы лучше поймёте модель объектов в Python и узнаете, почему в этом языке на самом деле не существуют указатели. На случай, если вам понадобится сымитировать поведение указателей, вы научитесь эмулировать их без сопутствующего кошмара управления памятью.
С помощью этой статьи вы:
- Узнаете, почему в Python нет указателей.
- Узнаете разницу между переменными C и именами в Python.
- Научитесь эмулировать указатели в Python.
- С помощью ctypes поэкспериментируете с настоящими указателями.
Почему в Python нет указателей?
Не знаю. Могут ли указатели существовать в Python нативно? Вероятно, но судя по всему, указатели противоречат понятию Zen of Python, потому что провоцируют неявные изменения вместо явных. Нередко указатели довольно сложны, особенно для новичков. Более того, они подталкивают вас к неудачным решениям или к тому, чтобы сделать что-нибудь действительно опасное, вроде чтения из области памяти, откуда вам не следовало считывать.
Python старается абстрагировать от пользователя подробности реализации, например адреса памяти. Часто в этом языке упор делается на удобство использования, а не на скорость. Поэтому указатели в Python не имеют особого смысла. Но не переживайте, по умолчанию язык предоставляет вам некоторые преимущества использования указателей.
Чтобы разобраться с указателями в Python, давайте кратко пройдёмся по особенностями реализации языка. В частности, вам нужно понять:
- Что такое изменяемые и неизменяемые объекты.
- Как устроены переменные/имена в Python.
Объекты в Python
Всё в Python является объектами. Например, откройте REPL и посмотрите, как используется isinstance() :
Этот код демонстрирует, что всё в Python — на самом деле объекты. Каждый объект содержит как минимум три вида данных:
- Счётчик ссылок.
- Тип.
- Значение.
Но не все объекты одинаковы. Есть одно важное отличие: объекты бывают изменяемые и неизменяемые. Понимание этого различия между типами объектов поможет вам лучше осознать первый слой луковицы, которая называется «указатели в Python».
Изменяемые и неизменяемые объекты
В Python есть два типа объектов:
- Неизменяемые объекты (не могут быть изменены);
- Изменяемые объекты (могут быть изменены).
Тип | Неизменяемый? |
---|---|
int | Да |
float | Да |
bool | Да |
complex | Да |
tuple | Да |
frozenset | Да |
str | Да |
list | Нет |
set | Нет |
dict | Нет |
Как видите, многие из часто используемых примитивных типов являются неизменяемыми. Проверить это можно, написав кое-какой код на Python. Вам понадобится два инструмента из стандартной библиотеки:
- id() возвращает адрес памяти объекта;
- is возвращает True , если и только если два объекта имеют одинаковый адрес памяти.
Здесь мы присвоили переменной x значение 5 . Если вы попробуете изменить значение с помощью сложения, то получите новый объект:
Хотя может показаться, что этот код просто меняет значение x , но на самом деле вы получаете в качестве ответа новый объект.
Тип str тоже неизменяем:
И в этом случае s после операции += получает другой адрес памяти.
Бонус: Оператор += преобразовывается в различные вызовы методов.
Для некоторых объектов, таких как список, += преобразует в __iadd__() (локальное добавление). Оно изменит себя и вернёт тот же ID. Однако у str и int нет этих методов, и в результате будет вызываться __add__() вместо __iadd__() .
Подробнее об этом рассказывается в документации по моделям данных Python.
При попытке напрямую изменить строковое значение s мы получим ошибку:
Обратная трассировка (последними отображаются самые свежие вызовы):
Приведённый выше код сбоит и Python сообщает, что str не поддерживает это изменение, что соответствует определению неизменяемости типа str .
Сравните с изменяемым объектом, например, со списком:
Этот код демонстрирует основное различие между двумя типами объектов. Изначально у my_list есть ID. Даже после добавления к списку 4 , my_list всё ещё имеет тот же ID. Причина в том, что тип list является изменяемым.
Вот ещё одна демонстрация изменяемости списка с помощью присваивания:
В этом коде мы изменили my_list и задали ему в качестве первого элемента 0 . Однако список сохранил тот же ID после этой операции. Следующим шагом на нашем пути к познанию Python будет исследование его экосистемы.
Разбираемся с переменными
Переменные в Python в корне отличаются от переменных в C и C++. По сути, их просто нет в Python. Вместо переменных здесь имена.
Это может звучать педантично, и по большей части так оно и есть. Чаще всего можно воспринимать имена в Python в качестве переменных, но необходимо понимать разницу. Это особенно важно, когда изучаешь такую непростую тему, как указатели.
Чтобы вам было проще разобраться, давайте посмотрим, как работают переменные в С, что они представляют, а затем сравним с работой имён в Python.
Переменные в C
Возьмём код, который определяет переменную x :
Исполнение это короткой строки проходит через несколько различных этапов:
- Выделение достаточного количества памяти для числа.
- Присвоение этому месту в памяти значения 2337 .
- Отображение, что x указывает на это значение.
Здесь переменная x имеет фальшивый адрес 0x7f1 и значение 2337 . Если позднее вам захочется изменить значение x , можете сделать так:
Этот код присваивает переменной x новое значение 2338 , тем самым перезаписывая предыдущее значение. Это означает, что переменная x изменяема. Обновлённая схема памяти для нового значения:
Обратите внимание, что расположение x не поменялось, только само значение. Это важно. Нам это говорит о том, что x — это место в памяти, а не просто имя.
Можно также рассматривать этот вопрос в рамках концепции владения. С одной стороны, x владеет местом в памяти. Во-первых, x — это пустая коробка, которая может содержать лишь одно число (integer), в котором могут храниться целочисленные значения.
Когда вы присваиваете x какое-то значение, вы тем самым помещаете значение в коробку, принадлежащую x . Если вы хотите представить новую переменную y , то можете добавить такую строку:
Этот код создаёт новую коробку под названием y и копирует в неё значение из x . Теперь схема памяти выглядит так:
Обратите внимание на новое местоположение y — 0x7f5 . Хотя в y и было скопировано значение x , однако переменная y владеет новым адресом в памяти. Следовательно, вы можете перезаписывать значение y , не влияя на x :
Теперь схема памяти выглядит так:
Повторюсь: вы изменили значение y , но не местоположение. Кроме того, вы никак не повлияли на исходную переменную x .
С именами в Python совершенно иная ситуация.
Имена в Python
В Python нет переменных, вместо них имена. Вы можете на своё усмотрение использовать термин «переменные», однако важно знать разницу между переменными и именами.
Давайте возьмём эквивалентный код из вышеприведённого примера на С и напишем его на Python:
Как и в C, в ходе исполнения этого код проходит несколько отдельных этапов:
- Создаётся PyObject.
- Числу для PyObject’а присваивается typecode.
- 2337 присваивается значение для PyObject’а.
- Создаётся имя x .
- x указывает на новый PyObject.
- Счётчик ссылок PyObject’а увеличивается на 1.
PyObject определяется как C-структура, так что если вы удивляетесь, почему нельзя напрямую вызвать typecode или счётчик ссылок, то причина в том, что у вас нет прямого доступа к структурам. Вызовы методов вроде sys.getrefcount() могут помочь получить какие-то внутренние вещи.
Если говорить о памяти, то это может выглядеть таким образом:
Здесь схема памяти сильно отличается от схемы в С, показанной выше. Вместо того, чтобы x владел блоком памяти, в котором хранится значение 2337 , свежесозданный объект Python владеет памятью, в которой живёт 2337 . Python-имя x не владеет напрямую каким-либо адресом в памяти, как С-переменная владеет статической ячейкой.
Если хотите присвоить x новое значение, попробуйте такой код:
Поведение системы будет отличаться от того, что происходит в С, но будет не слишком сильно отличаться от исходной привязки (bind) в Python.
- Создаётся новый PyObject.
- Числу для PyObject’а присваивается typecode.
- 2 присваивается значение для PyObject’а.
- x указывает на новый PyObject.
- Счётчик ссылок нового PyObject увеличивается на 1.
- Счётчик ссылок старого PyObject уменьшается на 1.
Эта иллюстрация демонстрирует, что x указывает на ссылку на объект и не владеет областью памяти, как раньше. Также вы видите, что команда x = 2338 является не присваиванием, а, скорее, привязкой (binding) имени x к ссылке.
Кроме того, предыдущий объект (содержавший значение 2337 ) теперь находится в памяти со счётчиком ссылок, равным 0, и будет убран сборщиком мусора.
Вы можете ввести новое имя y , как в примере на С:
В памяти появится новое имя, но не обязательно новый объект:
Теперь вы видите, что новый Python-объект не создан, создано только новое имя, которое указывает на тот же объект. Кроме того, счётчик ссылок объекта увеличился на 1. Можете проверить эквивалентность идентичности объектов, чтобы подтвердить их одинаковость:
Этот код показывает, что x и y являются одним объектом. Но не ошибитесь: y всё ещё является неизменяемым. Например, вы можете выполнить с y операцию сложения:
После вызова сложения, вам вернётся новый Python-объект. Теперь память выглядит так:
Был создан новый объект, и y теперь указывает на него. Любопытно, что точно такое же конечное состояние мы получили бы, если напрямую привязали y к 2339 :
После этого выражения мы получим такое конечное состояние памяти, как и при операции сложения. Напомню, что в Python вы не присваиваете переменные, а привязываете имена к ссылкам.
Об интернированных (intern) объектах в Python
Теперь вы понимаете, как создаются новые объекты в Python и как к ним привязываются имена. Пришло время поговорить об интернированных (interned) объектах.
У нас есть такой Python-код:
Как и раньше, x и y являются именами, указывающими на один и тот же Python-объект. Но это объект, содержащий значение 1000 , не может всегда иметь одинаковый адрес памяти. Например, если вы сложили два числа и получили 1000, то получите другой адрес:
На этот раз строка x is y возвращает False . Если вас это смутило, не беспокойтесь. Вот что происходит при исполнении этого кода:
- Создаётся Python-объект ( 1000 ).
- Ему присваивается имя x .
- Создаётся Python-объект ( 499 ).
- Создаётся Python-объект ( 501 ).
- Эти два объекта складываются.
- Создаётся новый Python-объект ( 1000 ).
- Ему присваивается имя y .
Причина в сообразительности компилятора CPython, который старается выполнить peephole-оптимизации, помогающие по мере возможности экономить шаги исполнения кода. Подробности вы можете найти в исходном коде peephole-оптимизатора CPython.
Но разве это не расточительно? Ну да, но эту цену вы платите за все замечательные преимущества Python. Вам не нужно думать об удалении подобных промежуточных объектов, и даже не нужно знать об их существовании! Прикол в том, что эти операции выполняются относительно быстро, и вы бы о них не узнали до этого момента.
Создатели Python мудро подметили эти накладные расходы и решили сделать несколько оптимизаций. Их результатом является поведение, которое может удивить новичков:
В этом примере почти такой же код, как и выше, за исключением того, что мы получаем True . Всё дело в интернированных (interned) объектах. Python предварительно создаёт в памяти определённое подмножество объектов и хранит их в глобальном пространстве имён для повседневного использования.
Какие объекты зависят от реализации Python? В CPython 3.7 интернированными являются:
- Целые числа в диапазоне от -5 до 256 .
- Строки, содержащие только ASCII-буквы, цифры или знаки подчёркивания.
Строки размером меньше 20 символов и содержащие ASCII-буквы, цифры или знаки подчёркивания будут интернированы, поскольку предполагается, что они будут применяться в качестве идентификаторов:
Здесь s1 и s2 указывают на один и тот же адрес в памяти. Если бы мы вставили не ASCII-букву, цифру или знак подчёркивания, то получили бы другой результат:
В этом примере использован восклицательный знак, поэтому строки не интернированы и являются разными объектами в памяти.
Бонус: Если хотите, чтобы эти объекты ссылались на один и тот же интернированный объект, то можете воспользоваться sys.intern() . Один из способов применения этой функции описан в документации:
Интернированные объекты часто путают программистов. Просто запомните, что если начнёте сомневаться, то всегда можете воспользоваться id() и is для определения эквивалентности объектов.
Эмулирование указателей в Python
Тот факт, что указатели в Python отсутствуют нативно, не означает, что вы не можете воспользоваться преимуществами применения указателей. На самом деле есть несколько способов эмулирования указателей в Python. Здесь мы рассмотрим два из них:
- Применение в качестве указателей изменяемых типов.
- Применение специально подготовленных Python-объектов.
Применение в качестве указателей изменяемых типов
Вы уже знаете, что такое изменяемые типы. Именно благодаря их изменяемости мы можем эмулировать поведение указателей. Допустим, нужно реплицировать этот код:
Этот код берёт указатель на число ( *x ) и инкрементирует значение на 1. Вот основная функция для исполнения кода:
В приведённом фрагменте мы присвоили y значение 2337 , вывели на экран текущее значение, увеличили его на 1, а затем вывели новое значение. На экране появляется:
Один из способов репликации этого поведения в Python — использовать изменяемый тип. Например, применить список и изменить первый элемент:
Здесь add_one(x) обращается к первому элементу и увеличивает его значение на 1. Применение списка означает, что в результате мы получим изменённое значение. Так значит в Python существуют указатели? Нет. Описанное поведение стало возможным потому, что список — это изменяемый тип. Если вы попытаетесь использовать кортеж, то получите ошибку:
Обратная трассировка (последними идут самые свежие вызовы):
Этот код демонстрирует неизменяемость кортежа, поэтому он не поддерживает присваивание элементов.
list не единственный изменяемый тип, указатели части эмулируются и с помощью dict .
Допустим, у вас есть приложение, которое должно отслеживать возникновение интересных событий. Это можно сделать с помощью создания словаря и использования одного из его элементов в качестве счётчика:
В этом примере словарь использует счётчики для отслеживания количества вызовов функции. После вызова foo() счётчик увеличился на 2, как и ожидалось. И всё благодаря изменяемости dict .
Не забывайте, это лишь эмуляция поведения указателя, оно никак не связано с настоящими указателями в C и C++. Можно сказать, эти операции обходятся дороже, чем если бы они выполнялись в C или C++.
Использование объектов Python
dict — прекрасный способ эмулирования указателей в Python, но иногда бывает утомительно помнить, какое имя ключа вы использовали. Особенно, если вы применяете словарь в разных частях приложения. Здесь может помочь настраиваемый класс Python.
Допустим, вам нужно отслеживать метрики в приложении. Отличный способ абстрагироваться от раздражающих подробностей — это создать класс:
В этом коде определён класс Metrics . Он всё ещё использует словарь для хранения актуальных данных, которые лежат в переменной члена _metrics . Это даст вам требуемую изменяемость. Теперь нужно лишь получить доступ к этим значениям. Можно сделать это с помощью свойств:
Здесь мы используем @property. Если вы не знакомы с декораторами, то почитайте статью Primer on Python Decorators. В данном случае декоратор @property позволяет обратиться к func_calls и cat_pictures_served , как если бы они были атрибутами:
То, что вы можете обратиться к этим именам как к атрибутам, означает, что вы абстрагированы от факта, что эти значения хранятся в словаре. К тому же вы делаете имена атрибутов более явными. Конечно, у вас должна быть возможность увеличивать значения:
Мы ввели два новых метода:
- inc_func_calls()
- inc_cat_pics()
Вы можете обращаться к func_calls и вызывать inc_func_calls() в разных частях приложений и эмулировать указатели в Python. Это полезно в ситуациях, когда у вас есть что-то вроде metrics , что нужно часто использовать и обновлять в разных частях приложений.
Примечание: В данном случае, явное создание inc_func_calls() и inc_cat_pics() вместо использования @property.setter не даёт пользователям задавать эти значения произвольному int , или неправильное значение вроде словаря.
Вот полный исходный код класса Metrics :
Реальные указатели с помощью ctypes
Может быть, всё-таки есть указатели в Python, особенно в CPython? С помощью встроенного модуля ctypes можно создать настоящие указатели, как в C. Если вы не знакомы с ctypes, можете почитать статью Extending Python With C Libraries and the «ctypes» Module.
Вам это может понадобиться в тех случаях, когда нужно вызвать библиотеку С, которой необходимы указатели. Вернёмся к упомянутой выше С-функции add_one() :
Напомню, что этот код увеличивает значение x на 1. Чтобы им воспользоваться, сначала скомпилируем код в общий (shared) объект. Будем считать, что наш файл хранится в add.c , сделать это можно с помощью gcc:
Первая команда компилирует исходный файл C в объект add.o . Вторая команда берёт этот несвязанный объект и создаёт общий объект libadd1.so .
libadd1.so должен лежать в вашей текущей директории. Можете с помощью ctypes загрузить его в Python:
Код ctypes.CDLL возвращает объект, который представляет общий объект libadd1 . Поскольку в нём вы определили add_one() , вы можете обращаться к этой функции, как если бы это был любой другой Python-объект. Но прежде чем вызывать функцию, нужно определить её сигнатуру. Так Python будет знать, что вы передаёте функции правильный тип.
В нашем случае сигнатурой функции является указатель на число, ctypes позволит задать это с помощью такого кода:
Здесь мы задаём сигнатуру функции, чтобы удовлетворить ожиданиям C. Теперь, если попробуем вызвать этот код с неправильным типом, то вместо непредсказуемого поведения получим красивое предупреждение:
Python бросает ошибку и объясняет, что add_one() хочет получить указатель, а не просто целое число. К счастью, в ctypes есть способ передавать указатели таким функциям. Сначала объявим целое число в стиле С:
Здесь мы создали целое число x со значением 0 . ctypes предоставляет удобную функцию byref() , которая позволяет передавать переменную по ссылке.
Примечание: Словосочетание по ссылке является антонимом передаче переменной по значению.
При передаче по ссылке вы передаёте ссылку на исходную переменную, поэтому изменения будут отражены и на ней. При передаче по значению вы получаете копию исходной переменной, и изменения эту исходную переменную уже не затрагивают.
Для вызова add_one() можете использовать этот код:
Отлично! Ваше число увеличилось на 1. Поздравляю, вы успешно использовали в Python настоящие указатели.
Заключение
Теперь вы лучше понимаете взаимосвязь между объектами Python и указателями. Хотя некоторые уточнения касательно имён и переменных выглядят проявлениями педантизма, однако понимание сути эти ключевых терминов улучшает ваше понимание механизма обработки переменных в Python.