Вопрос:

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

python reference parameter-passing pass-by-reference

1137427 просмотра

26 ответа

20837 Репутация автора

Документация Python кажется неясной относительно того, передаются ли параметры по ссылке или по значению, и следующий код выдает неизменное значение «Original»

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

Что я могу сделать, чтобы передать переменную по фактической ссылке?

Автор: David Sykes Источник Размещён: 12.06.2009 10:23

Ответы (26)


17 плюса

2164 Репутация автора

В этом случае переменной, названной varв методе Change, присваивается ссылка self.variable, и вы немедленно присваиваете строку var. Это больше не указывает на self.variable. Следующий фрагмент кода показывает, что произойдет, если вы измените структуру данных, на которую указывает, varа self.variableв данном случае список:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

Я уверен, что кто-то еще мог бы уточнить это дальше.

Автор: Mike Mazur Размещён: 12.06.2009 10:39

2516 плюса

161113 Репутация автора

Решение

Аргументы передаются по назначению . Обоснование этого двоякое:

  1. переданный параметр на самом деле является ссылкой на объект (но ссылка передается по значению)
  2. некоторые типы данных являются изменчивыми, а другие нет

Так:

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

  • Если вы передадите неизменный объект методу, вы все равно не сможете перепривязать внешнюю ссылку и даже не можете изменить объект.

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

Список - изменяемый тип

Давайте попробуем изменить список, который был передан методу:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

Выход:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

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

Теперь давайте посмотрим, что происходит, когда мы пытаемся изменить ссылку, переданную в качестве параметра:

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

Выход:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

Поскольку the_listпараметр был передан по значению, присвоение ему нового списка не имело эффекта, который мог видеть код вне метода. Это the_listбыла копия outer_listссылки, и у нас был the_listуказатель на новый список, но не было никакого способа изменить, где outer_listуказано.

Строка - неизменный тип

Он неизменен, поэтому мы ничего не можем сделать, чтобы изменить содержимое строки

Теперь давайте попробуем изменить ссылку

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

Выход:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

Опять же, поскольку the_stringпараметр был передан по значению, присвоение ему новой строки не имело эффекта, который мог видеть код вне метода. Это the_stringбыла копия outer_stringссылки, и у нас была the_stringуказатель на новую строку, но не было никакого способа изменить, где outer_stringуказано.

Надеюсь, это немного прояснит ситуацию.

РЕДАКТИРОВАТЬ: Было отмечено, что это не отвечает на вопрос, который первоначально задавал @David, «Есть ли что-то, что я могу сделать, чтобы передать переменную по фактической ссылке?». Давайте работать над этим.

Как мы можем обойти это?

Как показывает ответ @ Andrea, вы можете вернуть новое значение. Это не меняет способ передачи данных, но позволяет получить информацию, которую вы хотите вернуть:

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

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

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

Хотя это кажется немного громоздким.

Автор: Blair Conrad Размещён: 12.06.2009 11:18

23 плюса

36501 Репутация автора

У вас есть действительно хорошие ответы здесь.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )
Автор: bobobobo Размещён: 12.06.2009 12:16

157 плюса

43950 Репутация автора

Подумайте о вещах, передаваемых по присваиванию, а не по ссылке / по значению. Таким образом, всегда понятно, что происходит, пока вы понимаете, что происходит во время обычного задания.

Таким образом, при передаче списка в функцию / метод, список присваивается имени параметра. Добавление к списку приведет к изменению списка. Переназначение списка внутри функции не изменит первоначальный список, поскольку:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Поскольку неизменяемые типы не могут быть изменены, они, похоже , передаются по значению - передача int в функцию означает присвоение int параметру functions. Вы можете только переназначить это, но это не изменит значение исходных переменных.

Автор: Daren Thomas Размещён: 12.06.2009 12:17

227 плюса

58768 Репутация автора

Это не передача по значению или передача по ссылке - это вызов по объекту. Посмотрите на это, Фредрик Лунд:

http://effbot.org/zone/call-by-object.htm

Вот важная цитата:

«... переменные [имена] не являются объектами; их нельзя обозначать другими переменными или ссылаться на объекты».

В вашем примере, когда Changeметод вызывается - для него создается пространство имен ; и varстановится именем в этом пространстве имен для строкового объекта 'Original'. Этот объект затем имеет имя в двух пространствах имен. Затем var = 'Changed'связывается varс новым строковым объектом, и, таким образом, пространство имен метода забывает о 'Original'. Наконец, это пространство имен забыто, и строка 'Changed'вместе с ним.

Автор: David Cournapeau Размещён: 12.06.2009 12:55

36 плюса

5164 Репутация автора

(изменить - Блэр обновил свой чрезвычайно популярный ответ, чтобы он стал точным)

Я думаю, что важно отметить, что текущий пост, набравший наибольшее количество голосов (Блэр Конрад), будучи верным в отношении своего результата, вводит в заблуждение и является некорректным на основе его определений. Хотя существует много языков (например, C), которые позволяют пользователю передавать по ссылке или по значению, Python не является одним из них.

Ответ Дэвида Курнапо указывает на реальный ответ и объясняет, почему поведение в посте Блэр Конрад кажется правильным, а определения - нет.

В той степени, в которой Python передается по значению, все языки передаются по значению, поскольку должен быть отправлен некоторый фрагмент данных (будь то «значение» или «ссылка»). Тем не менее, это не означает, что Python передается по значению в том смысле, что программист на Си может думать об этом.

Если вы хотите поведение, ответ Блэр Конрад в порядке. Но если вы хотите знать, почему Python не передается по значению или по ссылке, прочитайте ответ Дэвида Курнапо.

Автор: KobeJohn Размещён: 27.06.2010 12:33

41 плюса

3391 Репутация автора

Простой трюк, который я обычно использую, - это просто обернуть его в список:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(Да, я знаю, что это может быть неудобно, но иногда это достаточно просто сделать.)

Автор: AmanicA Размещён: 05.08.2011 10:52

607 плюса

229719 Репутация автора

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

a = 1
a = 2

Вы полагаете, что aэто ячейка памяти, в которой хранится значение 1, а затем обновляется для хранения значения 2. Это не так, как все работает в Python. Скорее, aначинается как ссылка на объект со значением 1, а затем переназначается как ссылка на объект со значением 2. Эти два объекта могут продолжать сосуществовать, даже если они aбольше не относятся к первому; фактически они могут быть разделены любым количеством других ссылок в программе.

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

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variableявляется ссылкой на строковый объект 'Original'. При вызове Changeвы создаете вторую ссылку varна объект. Внутри функции вы переназначаете ссылку varна другой строковый объект 'Changed', но ссылка self.variableявляется отдельной и не изменяется.

Единственный способ обойти это - передать изменяемый объект. Поскольку обе ссылки ссылаются на один и тот же объект, любые изменения объекта отражаются в обоих местах.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'
Автор: Mark Ransom Размещён: 15.11.2011 05:45

58 плюса

13059 Репутация автора

Технически, Python всегда использует проход по ссылочным значениям . Я собираюсь повторить мой другой ответ, чтобы поддержать мое утверждение.

Python всегда использует значения передачи по ссылке. Нет никаких исключений. Любое присвоение переменной означает копирование контрольного значения. Не исключение Любая переменная - это имя, связанное с эталонным значением. Всегда.

Вы можете думать о ссылочном значении как адрес целевого объекта. Адрес автоматически разыменовывается при использовании. Таким образом, работая с эталонным значением, кажется, что вы работаете непосредственно с целевым объектом. Но между ними всегда есть ссылка, еще один шаг, чтобы перейти к цели.

Вот пример, который доказывает, что Python использует передачу по ссылке:

Иллюстрированный пример передачи аргумента

Если аргумент был передан по значению, внешний lstне может быть изменен. Зеленый - это целевые объекты (черный - это значение, хранящееся внутри, красный - это тип объекта), желтый - это память с эталонным значением внутри - нарисованная как стрелка. Синяя сплошная стрелка - это эталонное значение, которое было передано функции (через пунктирный путь синей стрелки). Уродливый темно-желтый - это внутренний словарь. (На самом деле его можно нарисовать также в виде зеленого эллипса. Цвет и форма говорят только о том, что он внутренний.)

Вы можете использовать id()встроенную функцию, чтобы узнать, что такое эталонное значение (то есть адрес целевого объекта).

В скомпилированных языках переменная - это область памяти, которая может захватывать значение типа. В Python переменная - это имя (захваченное внутри как строка), привязанное к ссылочной переменной, которая содержит ссылочное значение для целевого объекта. Имя переменной - это ключ во внутреннем словаре, часть значения этого элемента словаря хранит эталонное значение для цели.

Справочные значения скрыты в Python. Не существует явного пользовательского типа для хранения ссылочного значения. Тем не менее, вы можете использовать элемент списка (или элемент в любом другом подходящем типе контейнера) в качестве ссылочной переменной, потому что все контейнеры хранят элементы также как ссылки на целевые объекты. Другими словами, элементы на самом деле не содержатся внутри контейнера - только ссылки на элементы.

Автор: pepr Размещён: 15.09.2012 06:53

9 плюса

13853 Репутация автора

Вот простое (я надеюсь) объяснение концепции, pass by objectиспользуемой в Python.
Всякий раз, когда вы передаете объект в функцию, сам объект передается (объект в Python - это фактически то, что вы бы назвали значением в других языках программирования), а не ссылка на этот объект. Другими словами, когда вы звоните:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

The actual object - [0, 1] (which would be called a value in other programming languages) is being passed. So in fact the function change_me will try to do something like:

[0, 1] = [1, 2, 3]

which obviously will not change the object passed to the function. If the function looked like this:

def change_me(list):
   list.append(2)

Then the call would result in:

[0, 1].append(2)

which obviously will change the object. This answer explains it well.

Автор: matino Размещён: 02.10.2012 08:03

59 плюса

139005 Репутация автора

Эффбот (он же Фредрик Лунд) описал стиль передачи переменных в 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" (точно так же, как обновление базы данных будет видно для каждого подключения к этой базе данных).

Надеюсь, это прояснит проблему для вас.

Автор: Raymond Hettinger Размещён: 29.03.2013 04:41

15 плюса

1042 Репутация автора

Как вы можете утверждать, у вас должен быть изменяемый объект, но позвольте мне предложить вам проверить глобальные переменные, так как они могут помочь вам или даже решить эту проблему!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

пример:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2
Автор: Nuno Aniceto Размещён: 10.02.2014 05:57

37 плюса

19108 Репутация автора

В Python нет переменных

Ключ к пониманию передачи параметров - перестать думать о «переменных». В Python есть имена и объекты, и вместе они выглядят как переменные, но полезно всегда различать три.

  1. У Python есть имена и объекты.
  2. Назначение привязывает имя к объекту.
  3. Передача аргумента в функцию также связывает имя (имя параметра функции) с объектом.

Это все, что нужно сделать. Изменчивость не имеет значения для этого вопроса.

Пример:

a = 1

Это связывает имя aс объектом типа integer, который содержит значение 1.

b = x

Это связывает имя bс тем же объектом, с которым в xданный момент связано имя. Впоследствии имя bбольше не имеет ничего общего с именем x.

Смотрите разделы 3.1 и 4.2 в справочнике по языку Python 3.

Как прочитать пример в вопросе

В коде, показанном в вопросе, оператор self.Change(self.variable)связывает имя var(в области функции Change) с объектом, который содержит значение, 'Original'а присваивание var = 'Changed'(в теле функции Change) снова присваивает это же имя: какому-то другому объекту (что происходит держать строку также, но могло бы быть что-то еще полностью).

Как пройти по ссылке

(изменить 2019-04-28)

Так что, если вещь, которую вы хотите изменить, является изменяемым объектом, проблем не возникает, так как все эффективно передается по ссылке.

Если это неизменяемый объект (например, bool, число, строка), способ состоит в том, чтобы обернуть его в изменяемый объект.
Быстрое решение для этого - одноэлементный список (вместо self.variablepass [self.variable]и в функции modify var[0]).
Более питонический подход состоял бы в том, чтобы ввести тривиальный класс с одним атрибутом. Функция получает экземпляр класса и манипулирует атрибутом.

Автор: Lutz Prechelt Размещён: 11.02.2014 11:29

284 плюса

8044 Репутация автора

Я нашел другие ответы довольно длинными и сложными, поэтому я создал эту простую диаграмму, чтобы объяснить, как Python обрабатывает переменные и параметры. введите описание изображения здесь

Автор: Zenadix Размещён: 04.09.2014 04:05

13 плюса

4102 Репутация автора

Здесь есть много идей для ответов, но я думаю, что дополнительный момент здесь явно не упоминается. Цитирование из документации по Python https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

«В Python переменные, на которые ссылаются только внутри функции, неявно глобальны. Если переменной назначено новое значение где-либо в теле функции, она считается локальной. Если переменной когда-либо назначается новое значение внутри функции, переменная неявно локальна, и вам нужно явно объявить ее как «глобальную». Хотя сначала это немного удивляет, но это объясняется моментально. С одной стороны, требование глобальной для назначенных переменных обеспечивает защиту от непреднамеренных побочных эффектов. с другой стороны, если бы глобальный требовался для всех глобальных ссылок, вы бы использовали глобальный все время. Вы должны были бы объявить как глобальные все ссылки на встроенную функцию или компонент импортируемого модуля. Этот беспорядок будет побеждать полезность глобальной декларации для выявления побочных эффектов ".

Даже при передаче изменяемого объекта в функцию это все равно применяется. И мне ясно объясняется причина различий в поведении между назначением объекта и воздействием на объект в функции.

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

дает:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

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

Автор: Joop Размещён: 12.09.2014 02:40

17 плюса

4253 Репутация автора

Схема передачи по присваиванию в Python не совсем совпадает с опцией параметров ссылок C ++, но на практике она очень похожа на модель передачи аргументов языка C (и других):

  • Неизменяемые аргументы эффективно передаются « по значению» . Такие объекты, как целые числа и строки, передаются по ссылке на объект, а не путем копирования, но поскольку вы не можете изменить неизменяемые объекты на месте, эффект во многом напоминает создание копии.
  • Изменяемые аргументы эффективно передаются « по указателю» . Такие объекты, как списки и словари, также передаются по ссылке на объект, что аналогично тому, как C передает массивы как указатели - изменяемые объекты могут быть изменены на месте в функции, так же, как массивы C ,
Автор: ajknzhol Размещён: 27.03.2015 04:38

8 плюса

1029 Репутация автора

Aside from all the great explanations on how this stuff works in Python, I don't see a simple suggestion for the problem. As you seem to do create objects and instances, the pythonic way of handling instance variables and changing them is the following:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

В методах экземпляра вы обычно ссылаетесь selfна доступ к атрибутам экземпляра. Нормально устанавливать атрибуты экземпляра __init__и читать или изменять их в методах экземпляра. Вот почему вы также передаете selfпервый аргумент def Change.

Другое решение было бы создать статический метод, подобный этому:

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var
Автор: Dolf Andringa Размещён: 07.02.2016 11:18

6 плюса

675 Репутация автора

Есть небольшая хитрость, чтобы передать объект по ссылке, хотя язык не позволяет это сделать. Он работает и в Java, это список с одним элементом. ;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

Это безобразный хак, но это работает. ;-П

Автор: itmuckel Размещён: 21.04.2016 04:47

4 плюса

159 Репутация автора

Я использовал следующий метод, чтобы быстро преобразовать пару кодов Фортрана в Python. Правда, это не передача по ссылке, как был задан первоначальный вопрос, но в некоторых случаях это простая работа.

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c
Автор: Brad Porter Размещён: 08.08.2016 04:46

3 плюса

595 Репутация автора

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

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

Одним из способов является использование global(для глобальных переменных) или nonlocal(для локальных переменных в функции) в функции-обертке.

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

Та же идея работает для чтения и delполучения переменной.

Для простого чтения существует даже более короткий способ использования, lambda: xкоторый возвращает вызываемый объект, который при вызове возвращает текущее значение x. Это немного похоже на «звонок по имени», используемый в языках далекого прошлого.

Передача 3-х оболочек для доступа к переменной немного громоздка, поэтому их можно обернуть в класс с атрибутом proxy:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Поддержка «отражения» Pythons позволяет получить объект, который способен переназначить имя / переменную в заданной области, не определяя функции явно в этой области:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

Здесь ByRefкласс оборачивает словарь доступа. Таким образом, атрибут access to wrappedпереводится в элемент доступа в переданном словаре. Передав результат встроенной функции localsи имя локальной переменной, вы получите доступ к локальной переменной. Документация Python от 3.5 советует, что изменение словаря может не сработать, но мне кажется, что это работает.

Автор: textshell Размещён: 20.08.2016 02:02

3 плюса

93 Репутация автора

учитывая способ, которым python обрабатывает значения и ссылки на них, единственный способ ссылаться на произвольный атрибут экземпляра - по имени:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

в реальном коде вы, конечно, добавили бы проверку ошибок при поиске dict.

Автор: mARK bLOORE Размещён: 31.10.2016 03:33

2 плюса

2064 Репутация автора

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

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'
Автор: Jesse Hogan Размещён: 10.09.2017 02:19

0 плюса

3904 Репутация автора

Вы можете просто использовать пустой класс в качестве экземпляра для хранения ссылочных объектов, поскольку внутренние атрибуты объекта хранятся в словаре экземпляра. Смотрите пример.

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)
Автор: sergzach Размещён: 03.05.2018 02:14

2 плюса

106 Репутация автора

Передача по ссылке в Python сильно отличается от концепции передачи по ссылке в C ++ / Java.

  • Java & C #: типы примитивов (включая строку) передаются по значению (копия), тип ссылки передается по ссылке (копия адреса), поэтому все изменения, внесенные в параметр в вызываемой функции, видны вызывающей стороне.
  • C ++: допускается как передача по ссылке, так и передача по значению. Если параметр передается по ссылке, вы можете изменить его или нет, в зависимости от того, был ли параметр передан как const или нет. Однако, const или нет, параметр поддерживает ссылку на объект, и ссылка не может быть назначена для указания на другой объект в вызываемой функции.
  • Python: Python является «передачей по ссылке на объект», о которой часто говорят: «Ссылки на объекты передаются по значению». [Читать здесь] 1, И вызывающая сторона, и функция ссылаются на один и тот же объект, но параметр в функции - это новая переменная, которая просто хранит копию объекта в вызывающей стороне. Как и в C ++, параметр может быть изменен или не изменен в функции - это зависит от типа передаваемого объекта. например; Тип неизменяемого объекта не может быть изменен в вызываемой функции, тогда как изменяемый объект может быть либо обновлен, либо повторно инициализирован. Принципиальное различие между обновлением или повторным назначением / повторной инициализацией изменяемой переменной заключается в том, что обновленное значение отражается обратно в вызываемой функции, а реинициализированное значение - нет. Область любого назначения нового объекта изменяемой переменной является локальной для функции в питоне. Примеры, предоставленные @ blair-conrad, прекрасно это понимают.
Автор: Alok Garg Размещён: 22.01.2019 07:59

0 плюса

32 Репутация автора

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

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value
Автор: Liakos Размещён: 05.05.2019 02:14

0 плюса

12593 Репутация автора

Поскольку, кажется, нигде не упоминается, подход для имитации ссылок, известный, например, из C ++, состоит в том, чтобы использовать функцию «update» и передавать ее вместо фактической переменной (или, скорее, «name»):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

Это в основном полезно для «внешних ссылок» или в ситуации с несколькими потоками / процессами (делая безопасность функции обновления потока / многопроцессорности безопасной).

Очевидно, что вышеизложенное не позволяет прочитать значение, а только обновить его.

Автор: Daniel Jour Размещён: 10.05.2019 12:37
Вопросы из категории :
32x32