Как передать переменную по ссылке? - PullRequest
2341 голосов
/ 12 июня 2009

В документации Python неясно, передаются ли параметры по ссылке или по значению, и следующий код выдает неизменное значение 'Original'

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

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

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

Ответы [ 26 ]

23 голосов
/ 12 июня 2009

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

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' )
17 голосов
/ 12 июня 2009

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

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

17 голосов
/ 27 марта 2015

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

  • Неизменяемые аргументы эффективно передаются « по значению ». Такие объекты, как целые числа и строки, передаются по ссылке на объект, а не путем копирования, но поскольку вы все равно не можете изменить неизменяемые объекты на месте, эффект очень похоже на создание копии.
  • Изменяемые аргументы эффективно передаются « по указателю ». Такие объекты, как списки и словари также передаются по ссылке на объект, что аналогично способу C передает массивы как указатели - изменяемые объекты могут быть изменены на месте в функции, очень похоже на массивы C.
15 голосов
/ 10 февраля 2014

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

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
13 голосов
/ 12 сентября 2014

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

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

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

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

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

9 голосов
/ 02 октября 2012

Вот простое (я надеюсь) объяснение концепции 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)

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

8 голосов
/ 08 февраля 2016

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

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
6 голосов
/ 21 апреля 2016

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

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

4 голосов
/ 08 августа 2016

Я использовал следующий метод, чтобы быстро преобразовать пару кодов Фортрана в 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
3 голосов
/ 31 октября 2016

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

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

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

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

...