Подтвердить что ты не робот

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

Документация на Python кажется неясной о том, передаются ли параметры по ссылке или значению, а следующий код создает неизмененное значение "Оригинал"

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

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

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

4b9b3361

Ответ 1

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

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

Итак:

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

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

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

List - изменяемый тип

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

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.

Надеюсь, это немного облегчит.

EDIT: Было отмечено, что это не отвечает на вопрос, который первоначально задал @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])

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

Ответ 2

Проблема возникает из-за непонимания того, какие переменные находятся в 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'

Ответ 3

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

Ответ 4

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

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

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

"... variables [names] не являются объектами, они не могут быть обозначены другими переменными или называемыми объектами."

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

Ответ 5

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

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

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

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

Ответ 6

Effbot (aka Fredrik Lundh) описал стиль передачи переменной Python как вызов по-объекту: 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]. Пустой список был создан. Оба "x" и "y" указывают на один и тот же список. Метод append мутирует (обновляет) объект списка (например, добавляет запись в базу данных), и результат отображается как "x" , так и "y" (так же, как обновление базы данных будет видимым для каждого подключения к этой базе данных).

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

Ответ 7

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

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

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

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

Illustrated example of passing the argument

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

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

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

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

Ответ 8

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

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

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

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

Пример:

a = 1

Это связывает имя a с объектом целочисленного типа, который содержит значение 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.variable, передайте [self.variable] и в функции измените var[0]).
Более питонический подход заключается во введении тривиального класса с одним атрибутом. Функция получает экземпляр класса и манипулирует атрибутом.

Ответ 9

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

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

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

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

Ответ 10

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

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

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

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

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

Ответ 11

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

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' )

Ответ 12

В этом случае переменной 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']
>>> 

Я уверен, что кто-то еще может прояснить это дальше.

Ответ 13

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

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

Ответ 14

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

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

Ответ 15

Много ответов в ответах здесь, но я думаю, что здесь неясно упоминается дополнительный пункт. Цитирование из документации 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

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

Ответ 16

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

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

my_list = [0, 1]
change_me(my_list)

Проходит фактический объект - [0, 1] (который будет называться значением в других языках программирования). Таким образом, функция change_me будет пытаться сделать что-то вроде:

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

который, очевидно, не изменит объект, переданный функции. Если функция выглядела так:

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

Тогда вызов вызовет:

[0, 1].append(2)

который, очевидно, изменит объект. Этот ответ объясняет это хорошо.

Ответ 17

Помимо всех замечательных объяснений того, как этот материал работает на Python, я не вижу простого предложения по этой проблеме. Как вы, кажется, создаете объекты и экземпляры, pythonic способ обработки переменных экземпляра и их изменения выглядит следующим образом:

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

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

В методах экземпляра вы обычно ссылаетесь на self для доступа к атрибутам экземпляра. Как правило, устанавливать атрибуты экземпляра в __init__ и читать или изменять их в методах экземпляра. Вот почему вы передаете self als первый аргумент 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

Ответ 18

Есть небольшой трюк, чтобы передать объект по ссылке, хотя язык не дает возможности. Он также работает на 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

Это уродливый взлом, но он работает.;-P

Ответ 19

Я использовал следующий метод, чтобы быстро преобразовать пару кодов Fortran в 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

Ответ 20

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

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

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

def change(wrapper):
    wrapper(7)

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

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

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

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

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 переносит доступ к словарю. Таким образом, доступ к атрибуту wrapped преобразуется в доступ к элементу в переданном словаре. Передавая результат встроенного locals и имя локальной переменной, он получает доступ к локальной переменной. Документация на python по версии 3.5 указывает, что изменение словаря может не работать, но, похоже, оно работает для меня.

Ответ 21

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

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

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

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

Ответ 22

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

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

Ответ 23

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

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'

Ответ 24

Поскольку, кажется, нигде не упоминается, подход для имитации ссылок, известный, например, из 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

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

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

Ответ 25

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

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)

Ответ 26

Поскольку словари передаются по ссылке, вы можете использовать переменную 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