Документация Python кажется неясной относительно того, передаются ли параметры по ссылке или по значению, и следующий код выдает неизменное значение «Original»
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
Что я могу сделать, чтобы передать переменную по фактической ссылке?
python
reference
parameter-passing
pass-by-reference
Дэвид Сайкс
источник
источник
Ответы:
Аргументы передаются по назначению . Обоснование этого двоякое:
Так:
Если вы передадите изменяемый объект в метод, метод получит ссылку на этот же объект, и вы сможете изменить его на радость своего сердца, но если вы перепривязаете ссылку в методе, внешняя область ничего не будет знать об этом, и после все готово, внешняя ссылка все еще будет указывать на исходный объект.
Если вы передадите неизменный объект методу, вы все равно не сможете перепривязать внешнюю ссылку и даже не можете изменить объект.
Чтобы было еще яснее, давайте приведем несколько примеров.
Список - изменяемый тип
Давайте попробуем изменить список, который был передан методу:
Вывод:
Поскольку переданный параметр является ссылкой
outer_list
, а не его копией, мы можем использовать методы списка мутаций, чтобы изменить его и отразить изменения во внешней области видимости.Теперь давайте посмотрим, что происходит, когда мы пытаемся изменить ссылку, переданную в качестве параметра:
Вывод:
Поскольку
the_list
параметр был передан по значению, присвоение ему нового списка не имело эффекта, который мог видеть код вне метода. Этоthe_list
была копияouter_list
ссылки, и у нас былthe_list
указатель на новый список, но не было никакого способа изменить, гдеouter_list
указано.Строка - неизменный тип
Он неизменен, поэтому мы ничего не можем сделать, чтобы изменить содержимое строки
Теперь давайте попробуем изменить ссылку
Вывод:
Опять же, поскольку
the_string
параметр был передан по значению, присвоение ему новой строки не имело эффекта, который мог видеть код вне метода. Этоthe_string
была копияouter_string
ссылки, и у нас былаthe_string
указатель на новую строку, но не было никакого способа изменить, гдеouter_string
указано.Надеюсь, это немного прояснит ситуацию.
РЕДАКТИРОВАТЬ: Было отмечено, что это не отвечает на вопрос, который первоначально задавал @David, «Есть ли что-то, что я могу сделать, чтобы передать переменную по фактической ссылке?». Давайте работать над этим.
Как мы можем обойти это?
Как показывает ответ @ Andrea, вы можете вернуть новое значение. Это не меняет способ передачи данных, но позволяет получить информацию, которую вы хотите вернуть:
Если вы действительно хотите избежать использования возвращаемого значения, вы можете создать класс для хранения вашего значения и передать его в функцию или использовать существующий класс, например, список:
Хотя это кажется немного громоздким.
источник
def Foo(alist): alist = [1,2,3]
будет изменять содержимое списка с точки зрения абонентов.Проблема возникает из-за неправильного понимания того, какие переменные есть в Python. Если вы привыкли к большинству традиционных языков, у вас есть ментальная модель того, что происходит в следующей последовательности:
Вы полагаете, что
a
это ячейка памяти, в которой хранится значение1
, а затем обновляется для хранения значения2
. Это не так, как все работает в Python. Скорее,a
начинается как ссылка на объект со значением1
, а затем переназначается как ссылка на объект со значением2
. Эти два объекта могут продолжать сосуществовать, даже если ониa
больше не относятся к первому; фактически они могут быть разделены любым количеством других ссылок в программе.Когда вы вызываете функцию с параметром, создается новая ссылка, которая ссылается на переданный объект. Она отделена от ссылки, которая использовалась в вызове функции, поэтому нет способа обновить эту ссылку и заставить ее ссылаться на новый объект. В вашем примере:
self.variable
является ссылкой на строковый объект'Original'
. Когда вы звоните,Change
вы создаете вторую ссылкуvar
на объект. Внутри функции вы переназначаете ссылкуvar
на другой строковый объект'Changed'
, но ссылкаself.variable
является отдельной и не изменяется.Единственный способ обойти это - передать изменяемый объект. Поскольку обе ссылки ссылаются на один и тот же объект, любые изменения объекта отражаются в обоих местах.
источник
Я нашел другие ответы довольно длинными и сложными, поэтому я создал эту простую диаграмму, чтобы объяснить, как Python обрабатывает переменные и параметры.
источник
B[0] = 2
, против прямого назначенияB = 2
.A=B
илиB=A
.Это не передача по значению или передача по ссылке - это вызов по объекту. Посмотрите на это, Фредрик Лунд:
http://effbot.org/zone/call-by-object.htm
Вот важная цитата:
В вашем примере, когда
Change
метод вызывается - для него создается пространство имен ; иvar
становится именем в этом пространстве имен для строкового объекта'Original'
. Этот объект затем имеет имя в двух пространствах имен. Затемvar = 'Changed'
связываетсяvar
с новым строковым объектом, и, таким образом, пространство имен метода забывает о'Original'
. Наконец, это пространство имен забыто, и строка'Changed'
вместе с ним.источник
swap
функции, которая может поменять две ссылки, например:a = [42] ; b = 'Hello'; swap(a, b) # Now a is 'Hello', b is [42]
Подумайте о вещах, передаваемых по присваиванию, а не по ссылке / по значению. Таким образом, всегда ясно, что происходит, пока вы понимаете, что происходит во время обычного задания.
Таким образом, при передаче списка в функцию / метод, список присваивается имени параметра. Добавление к списку приведет к изменению списка. Переназначение списка внутри функции не изменит первоначальный список, поскольку:
Поскольку неизменяемые типы не могут быть изменены, они, похоже , передаются по значению - передача int в функцию означает присвоение int параметру функции. Вы можете только переназначить это, но это не изменит первоначальное значение переменных.
источник
Эффбот (он же Фредрик Лунд) описал стиль передачи переменных в Python как call-by-object: http://effbot.org/zone/call-by-object.htm
Объекты размещаются в куче, а указатели на них можно передавать где угодно.
Когда вы делаете присвоение, такое как
x = 1000
, создается словарная запись, которая отображает строку «x» в текущем пространстве имен на указатель на целочисленный объект, содержащий тысячу.Когда вы обновляете «x»
x = 2000
, создается новый целочисленный объект, а словарь обновляется, чтобы указывать на новый объект. Старый объект тысяча неизменен (и может или не может быть живым в зависимости от того, относится ли что-либо еще к объекту).Когда вы делаете новое назначение, например
y = x
, создается новая словарная запись "y", которая указывает на тот же объект, что и запись для "x".Объекты, такие как строки и целые числа, являются неизменными . Это просто означает, что нет методов, которые могут изменить объект после его создания. Например, когда создается целочисленный объект тысяча, он никогда не изменится. Математика делается путем создания новых целочисленных объектов.
Такие объекты, как списки, являются изменчивыми . Это означает, что содержимое объекта может быть изменено любым указателем на объект. Например,
x = []; y = x; x.append(10); print y
напечатает[10]
. Пустой список создан. И «х», и «у» указывают на один и тот же список. Метод append изменяет (обновляет) объект списка (например, добавляет запись в базу данных), и результат виден как "x", так и "y" (точно так же, как обновление базы данных будет видно для каждого соединения с этой базой данных).Надеюсь, это прояснит проблему для вас.
источник
id()
функция возвращает значение указателя (ссылки на объект), как предполагает ответ pepr?Технически, Python всегда использует проход по ссылочным значениям . Я собираюсь повторить мой другой ответ, чтобы поддержать мое утверждение.
Python всегда использует значения передачи по ссылке. Нет никаких исключений. Любое присвоение переменной означает копирование контрольного значения. Не исключение Любая переменная - это имя, связанное с эталонным значением. Всегда.
Вы можете думать о ссылочном значении как адрес целевого объекта. Адрес автоматически разыменовывается при использовании. Таким образом, работая со ссылочным значением, вы, кажется, работаете непосредственно с целевым объектом. Но между ними всегда есть ссылка, еще один шаг, чтобы перейти к цели.
Вот пример, который доказывает, что Python использует передачу по ссылке:
Если аргумент был передан по значению, внешний
lst
не может быть изменен. Зеленый - это целевые объекты (черный - это значение, хранящееся внутри, красный - это тип объекта), желтый - это память с эталонным значением внутри - нарисованная как стрелка. Синяя сплошная стрелка - это контрольное значение, которое было передано функции (через пунктирную синюю стрелку). Уродливый темно-желтый - это внутренний словарь. (На самом деле его можно нарисовать также в виде зеленого эллипса. Цвет и форма говорят только о том, что он внутренний.)Вы можете использовать
id()
встроенную функцию, чтобы узнать, что такое эталонное значение (то есть адрес целевого объекта).В скомпилированных языках переменная - это область памяти, которая может захватывать значение типа. В Python переменная - это имя (захваченное внутри как строка), привязанное к ссылочной переменной, которая содержит ссылочное значение для целевого объекта. Имя переменной - это ключ во внутреннем словаре, часть значения этого элемента словаря хранит эталонное значение для цели.
Справочные значения скрыты в Python. Не существует явного пользовательского типа для хранения ссылочного значения. Тем не менее, вы можете использовать элемент списка (или элемент в любом другом подходящем типе контейнера) в качестве ссылочной переменной, потому что все контейнеры хранят элементы также как ссылки на целевые объекты. Другими словами, элементы на самом деле не содержатся внутри контейнера - есть только ссылки на элементы.
источник
В Python нет переменных
Ключ к пониманию передачи параметров - перестать думать о «переменных». В Python есть имена и объекты, и вместе они выглядят как переменные, но полезно всегда различать три.
Это все, что нужно сделать. Изменчивость не имеет отношения к этому вопросу.
Пример:
Это связывает имя
a
с объектом типа integer, который содержит значение 1.Это связывает имя
b
с тем же объектом, с которым вx
данный момент связано имя. Впоследствии имяb
больше не имеет ничего общего с именемx
.Смотрите разделы 3.1 и 4.2 в справочнике по языку Python 3.
Как прочитать пример в вопросе
В коде, показанном в вопросе, оператор
self.Change(self.variable)
связывает имяvar
(в области функцииChange
) с объектом, который содержит значение,'Original'
а присваиваниеvar = 'Changed'
(в теле функцииChange
) снова присваивает это же имя: какому-то другому объекту (что происходит чтобы держать строку, но мог бы быть чем-то еще полностью).Как пройти по ссылке
Так что, если вещь, которую вы хотите изменить, является изменяемым объектом, проблем нет, так как все эффективно передается по ссылке.
Если это неизменяемый объект (например, bool, число, строка), способ состоит в том, чтобы обернуть его в изменяемый объект.
Быстрое и грязное решение для этого - одноэлементный список (вместо
self.variable
pass[self.variable]
и в функции modifyvar[0]
).Более питонический подход состоял бы в том, чтобы ввести тривиальный класс с одним атрибутом. Функция получает экземпляр класса и манипулирует атрибутом.
источник
int
переменная объявляется, ноInteger
нет. Они оба объявляют переменные.Integer
Переменная является объектом, тоint
переменная является примитивным. В качестве примера вы продемонстрировали, как работают ваши переменные, показавa = 1; b = a; a++ # doesn't modify b
. Это также верно в Python (используя,+= 1
поскольку++
в Python нет)!Простой трюк, который я обычно использую, - это просто обернуть его в список:
(Да, я знаю, что это может быть неудобно, но иногда это достаточно просто сделать.)
источник
(изменить - Блэр обновил свой чрезвычайно популярный ответ, чтобы он стал точным)
Я думаю, что важно отметить, что нынешний пост с наибольшим количеством голосов (Блэр Конрад), будучи верным в отношении своего результата, вводит в заблуждение и является неправильным на грани на основе его определений. Хотя существует много языков (например, C), которые позволяют пользователю передавать по ссылке или по значению, Python не является одним из них.
Ответ Дэвида Курнапо указывает на реальный ответ и объясняет, почему поведение в посте Блэр Конрад кажется правильным, а определения - нет.
В той степени, в которой Python передается по значению, все языки передаются по значению, поскольку должен быть отправлен некоторый фрагмент данных (будь то «значение» или «ссылка»). Тем не менее, это не означает, что Python передается по значению в том смысле, что программист на С может подумать об этом.
Если вы хотите поведение, ответ Блэр Конрад в порядке. Но если вы хотите знать, почему Python не передается по значению или по ссылке, прочитайте ответ Дэвида Курнапо.
источник
void swap(int& x, int& y) { int temp = x; x = y; y = temp; }
поменяется местами переданные ему переменные. В Паскале вы используетеvar
вместо&
.У вас есть действительно хорошие ответы здесь.
источник
В этом случае переменной, названной
var
в методеChange
, присваивается ссылкаself.variable
, и вы немедленно присваиваете строкуvar
. Это больше не указывает наself.variable
. Следующий фрагмент кода показывает, что произойдет, если вы измените структуру данных, на которую указывает,var
аself.variable
в данном случае список:Я уверен, что кто-то еще мог бы уточнить это дальше.
источник
Схема передачи по присваиванию в Python не совсем совпадает с опцией параметров ссылок C ++, но на практике она очень похожа на модель передачи аргументов языка C (и других):
источник
Как вы можете утверждать, у вас должен быть изменяемый объект, но позвольте мне предложить вам проверить глобальные переменные, поскольку они могут помочь вам или даже решить проблему такого рода!
http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python
пример:
источник
Здесь есть много идей для ответов, но я думаю, что дополнительный момент здесь явно не упоминается. Цитирование из документации по Python https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python
«В Python переменные, на которые ссылаются только внутри функции, неявно глобальны. Если переменной назначено новое значение где-либо в теле функции, она считается локальной. Если переменной когда-либо назначается новое значение внутри функции, переменная неявно локальна, и вам нужно явно объявить ее как «глобальную». Хотя сначала это немного удивляет, это объясняется моментально. С одной стороны, требование глобальной для назначенных переменных обеспечивает защиту от непреднамеренных побочных эффектов. с другой стороны, если бы глобальный требовался для всех глобальных ссылок, вы бы использовали глобальный все время. Вы должны были бы объявить глобальными все ссылки на встроенную функцию или компонент импортируемого модуля. Этот беспорядок будет побеждать полезность глобальной декларации для выявления побочных эффектов ".
Даже при передаче изменяемого объекта в функцию это все равно применяется. И мне ясно объясняется причина различий в поведении между назначением объекта и воздействием на объект в функции.
дает:
Следовательно, присвоение глобальной переменной, которая не объявлена глобальной, создает новый локальный объект и разрывает ссылку на исходный объект.
источник
Вот простое (я надеюсь) объяснение концепции,
pass by object
используемой в Python.Всякий раз, когда вы передаете объект в функцию, сам объект передается (объект в Python - это фактически то, что вы бы назвали значением в других языках программирования), а не ссылка на этот объект. Другими словами, когда вы звоните:
Фактический объект - [0, 1] (который будет называться значением в других языках программирования) передается. Так что на самом деле функция
change_me
будет пытаться сделать что-то вроде:что, очевидно, не изменит объект, переданный функции. Если функция выглядела так:
Тогда вызов приведет к:
что, очевидно, изменит объект. Этот ответ объясняет это хорошо.
источник
list = [1, 2, 3]
причинах реиспользования наlist
имя чего - то еще , и забывая первоначально переданный объект. Тем не менее, вы можете попробоватьlist[:] = [1, 2, 3]
(кстатиlist
, неверное имя для переменной. Думать о[0, 1] = [1, 2, 3]
том, что это полная чушь. В любом случае, что, по вашему мнению, означает, что сам объект передается ? Что, по вашему мнению, копируется в функцию?[0, 1] = [1, 2, 3]
это просто плохой пример. В Python нет ничего подобного.alist[2]
считается именем третьего элемента alist. Но я думаю, что неправильно понял, в чем заключалась ваша проблема. :-)Помимо всех замечательных объяснений того, как все это работает в Python, я не вижу простого предложения по проблеме. Как вы, похоже, создаете объекты и экземпляры, питонический способ обработки переменных экземпляра и их изменения заключается в следующем:
В методах экземпляра вы обычно ссылаетесь
self
на доступ к атрибутам экземпляра. Нормально устанавливать атрибуты экземпляра__init__
и читать или изменять их в методах экземпляра. Вот почему вы также передаетеself
первый аргументdef Change
.Другое решение было бы создать статический метод, подобный этому:
источник
Есть небольшая хитрость, чтобы передать объект по ссылке, хотя язык не позволяет это сделать. Он работает и в Java, это список с одним элементом. ;-)
Это безобразный хак, но это работает. ;-П
источник
p
является ссылкой на объект изменяемого списка, который в свою очередь хранит объектobj
. Ссылка 'p', передается вchangeRef
. ВнутриchangeRef
создается новая ссылка (называется новая ссылкаref
), которая указывает на тот же объект списка, на которыйp
указывает. Но поскольку списки изменчивы, изменения в списке видны по обеим ссылкам. В этом случае вы использовалиref
ссылку для изменения объекта с индексом 0, чтобы впоследствии он сохранялPassByReference('Michael')
объект. Изменение объекта списка было выполнено с использованием,ref
но это изменение видноp
.p
иref
указывают на объект списка , который хранит один объект,PassByReference('Michael')
. Итак, отсюда следует, чтоp[0].name
возвращаетсяMichael
. Конечно,ref
сейчас вышел за рамки и может быть мусор, но все же.name
исходногоPassByReference
объекта, связанного со ссылкойobj
. На самом деле,obj.name
вернетсяPeter
. Вышеупомянутые комментарии предполагают, что определениеMark Ransom
дало.PassByReference
объект другимPassByReference
объектом в своем списке и сослались на последний из двух объектов.Я использовал следующий метод, чтобы быстро преобразовать пару кодов Фортрана в Python. Правда, это не передача по ссылке, как был задан первоначальный вопрос, но в некоторых случаях это простая работа.
источник
dict
а затем возвращаетdict
. Однако во время очистки может стать очевидным, что требуется перестройка части системы. Следовательно, функция должна не только возвращать очищенные,dict
но и иметь возможность сигнализировать о восстановлении. Я пытался передатьbool
по ссылке, но OFC, который не работает. Выяснив, как решить эту проблему, я обнаружил, что ваше решение (в основном возвращающее кортеж) работает лучше всего, и при этом вообще не является взломом / обходным путем (IMHO).Хотя передача по ссылке - это не то, что хорошо вписывается в python и должно использоваться редко, есть некоторые обходные пути, которые на самом деле могут работать, чтобы получить объект, назначенный в настоящее время локальной переменной, или даже переназначить локальную переменную изнутри вызываемой функции.
Основная идея состоит в том, чтобы иметь функцию, которая может осуществлять такой доступ и может быть передана как объект в другие функции или сохранена в классе.
Одним из способов является использование
global
(для глобальных переменных) илиnonlocal
(для локальных переменных в функции) в функции-обертке.Та же идея работает для чтения и
del
получения переменной.Для простого чтения существует даже более короткий способ использования,
lambda: x
который возвращает вызываемый объект, который при вызове возвращает текущее значение x. Это немного похоже на «звонок по имени», используемый в языках далекого прошлого.Передача 3-х оболочек для доступа к переменной немного громоздка, поэтому их можно обернуть в класс с атрибутом proxy:
Поддержка «отражения» Pythons позволяет получить объект, который способен переназначить имя / переменную в заданной области, не определяя функции явно в этой области:
Здесь
ByRef
класс оборачивает словарь доступа. Таким образом, атрибут access towrapped
переводится в элемент доступа в переданном словаре. Передав результат встроенной функцииlocals
и имя локальной переменной, вы получите доступ к локальной переменной. Документация Python от 3.5 советует, что изменение словаря может не сработать, но мне кажется, что это работает.источник
учитывая то, как python обрабатывает значения и ссылается на них, единственный способ ссылаться на произвольный атрибут экземпляра - по имени:
в реальном коде вы, конечно, добавили бы проверку ошибок при поиске dict.
источник
Поскольку словари передаются по ссылке, вы можете использовать переменную dict для хранения любых ссылочных значений внутри нее.
источник
Передача по ссылке в Python сильно отличается от концепции передачи по ссылке в C ++ / Java.
источник
Поскольку ваш пример оказывается объектно-ориентированным, вы можете внести следующее изменение, чтобы получить аналогичный результат:
источник
Вы можете просто использовать пустой класс в качестве экземпляра для хранения ссылочных объектов, поскольку внутренние атрибуты объекта хранятся в словаре экземпляра. Смотрите пример.
источник
Поскольку, кажется, нигде не упоминается, подход для имитации ссылок, известный, например, из C ++, состоит в том, чтобы использовать функцию «update» и передавать ее вместо фактической переменной (или, скорее, «name»):
Это в основном полезно для «внешних ссылок» или в ситуации с несколькими потоками / процессами (делая функцию обновления потока / многопроцессорной безопасным).
Очевидно, что вышеизложенное не позволяет прочитать значение, а только обновить его.
источник