Jak przekazać zmienną przez odniesienie?

Dokumentacja Pythona wydaje się niejasna, czy parametry są przekazywane przez odniesienie czy wartość, a poniższy kod tworzy niezmienioną wartość "Original"

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

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

Czy jest coś, co mogę zrobić, aby przekazać zmienną przez rzeczywiste odniesienie?

Author: Taryn, 2009-06-12

23 answers

Argumenty są przekazywane przez przypisanie. Uzasadnienie tego jest dwojakie:

    W przeciwieństwie do innych obiektów, które nie są w stanie odczytać wartości, nie są one w stanie odczytać wartości.]}
  1. niektóre typy danych są zmienne, ale inne nie]}

Więc:

  • Jeśli przekazujesz mutowalny obiekt do metody, metoda otrzymuje odniesienie do tego samego obiektu i możesz go zmutować ku uciesze twojego serca, ale jeśli ponownie znajdź odniesienie w metodzie, zewnętrzny zakres nie będzie o tym wiedział, a po zakończeniu, zewnętrzne odniesienie nadal będzie wskazywać na oryginalny obiekt.

  • Jeśli przekazujesz obiekt niezmienny do metody, nadal nie możesz ponownie powiązać zewnętrznego odniesienia i nawet nie możesz zmutować obiektu.

Aby było to jeszcze bardziej jasne, podajmy kilka przykładów.

Lista-Typ zmienny

Spróbujmy zmodyfikować listę, która została przekazana do metody:

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)

Wyjście:

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

Ponieważ przekazany parametr jest odniesieniem do outer_list, a nie jego kopią, możemy użyć metod listy mutacji, aby zmienić go i mieć zmiany odzwierciedlone w zewnętrznym zakresie.

Teraz zobaczmy, co się stanie, gdy spróbujemy zmienić referencję przekazaną jako parametr:

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)

Wyjście:

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

Ponieważ parametr the_list został przekazany przez wartość, przypisując do niego nową listę nie miał wpływu na to, że kod spoza metody mógł zobaczyć. {[9] } był kopią outer_list referencji, i mieliśmy the_list wskazać na nową listę, ale nie było sposobu, aby zmienić miejsce outer_list wskazał.

String-Typ niezmienny

Jest niezmienny, więc nic nie możemy zrobić, aby zmienić zawartość łańcucha

Teraz spróbujmy zmienić odniesienie

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)

Wyjście:

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

Ponownie, ponieważ parametr the_string został przekazany według wartości przypisanie do niej nowego ciągu znaków nie miało wpływu na to, że kod spoza metody mógł zobaczyć. the_string był kopią referencji outer_string i mieliśmy the_string wskazywać na nowy ciąg znaków, ale nie było sposobu, aby zmienić miejsce outer_string wskazywane.

Mam nadzieję, że to trochę wyjaśni.

EDIT: zauważono, że nie odpowiada to na pytanie, które @ David pierwotnie zadał, " czy jest coś, co mogę zrobić, aby przekazać zmienną przez rzeczywiste odniesienie?". Popracujmy nad to.

Jak to obejdziemy?

Jak pokazuje odpowiedź @ Andrea, możesz zwrócić nową wartość. To nie zmienia sposobu przekazywania rzeczy, ale pozwala uzyskać informacje, które chcesz z powrotem: {]}

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)

Jeśli naprawdę chcesz uniknąć używania zwracanej wartości, możesz utworzyć klasę, która przechowuje twoją wartość i przekazuje ją do funkcji lub użyć istniejącej klasy, jak lista:

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])
Chociaż wydaje się to trochę kłopotliwe.
 2279
Author: Blair Conrad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-03 02:13:38

Problem wynika z niezrozumienia zmiennych w Pythonie. Jeśli jesteś przyzwyczajony do większości tradycyjnych języków, masz mentalny model tego, co dzieje się w następującej kolejności:

a = 1
a = 2

Uważasz, że {[3] } jest miejscem pamięci, które przechowuje wartość 1, a następnie jest aktualizowany do przechowywania wartości 2. W Pythonie tak nie działa. Zamiast tego a zaczyna się jako odniesienie do obiektu z wartością 1, a następnie zostaje ponownie przypisane jako odniesienie do obiektu z wartość 2. Te dwa obiekty mogą nadal współistnieć, mimo że a nie odnosi się już do pierwszego; w rzeczywistości mogą być współdzielone przez dowolną liczbę innych odniesień w programie.

Gdy wywołujesz funkcję z parametrem, tworzone jest nowe odniesienie, które odnosi się do przekazanego obiektu. Jest to oddzielone od odniesienia, które zostało użyte w wywołaniu funkcji, więc nie ma sposobu, aby zaktualizować to odniesienie i sprawić, by odnosiło się do nowego obiektu. W Twoim przykład:

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

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

self.variable jest odniesieniem do obiektu string 'Original'. Kiedy wywołujesz Change tworzysz drugie odniesienie var do obiektu. Wewnątrz funkcji przypisujesz referencję var do innego obiektu string 'Changed', ale Referencja self.variable jest oddzielna i nie zmienia się.

Jedynym sposobem na obejście tego jest przejście przez zmienny obiekt. Ponieważ oba odniesienia odnoszą się do tego samego obiektu, wszelkie zmiany w obiekcie są odzwierciedlane w obu miejscach.

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

def Change(self, var):
    var[0] = 'Changed'
 546
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-03 00:39:27

Znalazłem inne odpowiedzi dość długie i skomplikowane, więc stworzyłem ten prosty diagram, aby wyjaśnić sposób, w jaki Python traktuje zmienne i parametry. Tutaj wpisz opis obrazka

 253
Author: Zenadix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-25 15:30:05

Nie jest to pass-by-value ani pass-by-reference - jest to call-by-object. Zobacz też: Fredrik Lundh:

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

Oto znamienny cytat:

"...zmienne [nazwy] są obiektami a nie; nie mogą być oznaczane przez inne zmienne ani odwoływane przez obiekty."

W twoim przykładzie, gdy metoda Change jest wywołana--a przestrzeń nazw jest dla niej utworzona, a var staje się nazwą, wewnątrz przestrzeń nazw dla obiektu string 'Original'. Obiekt ten ma wtedy nazwę w dwóch przestrzeniach nazw. Następnie var = 'Changed' wiąże var z nowym obiektem string, a zatem przestrzeń nazw metody zapomina o 'Original'. W końcu ta przestrzeń nazw jest zapomniana, a łańcuch 'Changed' wraz z nim.

 221
Author: David Cournapeau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-26 09:06:43

Pomyśl o przekazywaniu rzeczy przez przypisanie zamiast przez odniesienie / przez wartość. W ten sposób jest zawsze jasne, co się dzieje, dopóki rozumiesz, co się dzieje podczas normalnego zadania.

Tak więc, podczas przekazywania listy do funkcji / metody, lista jest przypisana do nazwy parametru. Dodanie do listy spowoduje modyfikację listy. Zmiana listy wewnątrz funkcja nie zmieni oryginalnej listy, ponieważ:

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

Od immutable typy nie mogą być modyfikowane, wydają się być przekazywane przez wartość - przekazanie int do funkcji oznacza przypisanie int do parametru funkcji. Możesz tylko zmienić przypisanie, ale nie zmieni to wartości zmiennych początkowych.

 145
Author: Daren Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-12 12:17:48

Effbot (znany jako Fredrik Lundh) opisał zmienny styl przekazywania Pythona jako wywołanie przez obiekt: http://effbot.org/zone/call-by-object.htm

Obiekty są przydzielane na stercie i wskaźniki do nich mogą być przekazywane w dowolnym miejscu.

  • Kiedy wykonujesz przypisanie, takie jak x = 1000, tworzony jest wpis słownikowy, który mapuje łańcuch " x " w bieżącej przestrzeni nazw na wskaźnik do obiektu integer zawierającego tysiąc.

  • Po aktualizacji "x "z x = 2000, tworzony jest nowy obiekt integer i słownik jest aktualizowany tak, aby wskazywał na nowy obiekt. Stary obiekt tysiąc jest niezmieniony (i może, ale nie musi być żywy, w zależności od tego, czy cokolwiek innego odnosi się do obiektu).

  • Gdy wykonujesz nowe przypisanie, takie jak y = x, tworzony jest nowy wpis słownikowy "y", który wskazuje na ten sam obiekt, co wpis dla"x".

  • Obiekty takie jak łańcuchy i liczby całkowite są niezmienne. Oznacza to po prostu, że nie ma metod, które mogłyby zmienić obiekt po jego wytworzeniu. Na przykład, po utworzeniu obiektu integer one-thousand, nigdy się nie zmieni. Matematyka jest wykonywana przez tworzenie nowych obiektów całkowitych.

  • Obiekty takie jak listy są mutowalne . Oznacza to, że zawartość obiektu może być zmieniana przez cokolwiek wskazującego na obiekt. Na przykład, x = []; y = x; x.append(10); print y wydrukuje [10]. Pusta lista została utworzona. Zarówno "x", jak i " y " wskazują na tę samą listę. Na append metoda mutuje (aktualizuje) obiekt list (jak dodanie rekordu do bazy danych) i wynik jest widoczny zarówno dla "x" jak i " y " (tak jak aktualizacja bazy danych byłaby widoczna dla każdego połączenia z tą bazą danych).

Mam nadzieję, że to wyjaśni ci problem.

 54
Author: Raymond Hettinger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-29 04:41:44

Technicznie rzecz biorąc, Python zawsze używa wartości odniesienia pass by . Powtórzę moją drugą ODPOWIEDŹ , aby poprzeć moje oświadczenie.

Python zawsze używa wartości pass-by-reference. Nie ma żadnego wyjątku. Dowolne przypisanie zmiennej oznacza skopiowanie wartości referencyjnej. Bez wyjątku. Każda zmienna jest nazwą powiązaną z wartością odniesienia. Zawsze.

Można myśleć o wartości odniesienia jako adres obiektu docelowego. Adres jest automatycznie dereferowany, gdy używany. W ten sposób, pracując z wartością odniesienia, wydaje się, że pracujesz bezpośrednio z obiektem docelowym. Ale zawsze jest odniesienie pomiędzy, jeden krok więcej, aby przejść do celu.

Oto przykład, który dowodzi, że Python używa przekazywania przez odniesienie:

Ilustrowany przykład przekazania argumentu

Jeśli argument został przekazany przez wartość, zewnętrzna lst nie może zostać zmodyfikowana. Zielony to obiekty docelowe (czarny to wartość przechowywana wewnątrz, czerwony to typ obiektu), żółty to pamięć z wartością odniesienia wewnątrz -- rysowane jako strzałka. Niebieska strzałka jest wartością odniesienia, która została przekazana do funkcji (poprzez przerywaną niebieską ścieżkę strzałki). Brzydki Ciemnożółty jest wewnętrznym słownikiem. (Faktycznie moĹĽna jÄ ... narysowaÄ ‡ rĂłwnieĹĽ jako zielonÄ ... Elipse. Kolor i kształt mówią tylko, że jest wewnętrzny.)

Możesz użyć id() wbudowana funkcja, aby dowiedzieć się, jaka jest wartość odniesienia (czyli adres obiektu docelowego).

W językach skompilowanych, a zmienna jest przestrzenią pamięci, która jest w stanie uchwycić wartość typu. W Pythonie zmienna jest nazwą (przechwyconą wewnętrznie jako łańcuch znaków) powiązaną ze zmienną odniesienia, która przechowuje wartość odniesienia do obiektu docelowego. Nazwa zmiennej jest kluczem w wewnętrznym słowniku, część wartości tego elementu słownika przechowuje wartość odniesienia do celu.

Wartości odniesienia są ukryte w Pythonie. Nie ma żadnego jawnego typu użytkownika do przechowywania wartości referencyjnej. Jednakże, możesz użyć elementu listy (lub elementu w innym odpowiednim typie kontenera) jako zmiennej referencyjnej, ponieważ wszystkie kontenery przechowują elementy również jako odniesienia do obiektów docelowych. Innymi słowy, elementy w rzeczywistości nie są zawarte wewnątrz kontenera-tylko odniesienia do elementów są.

 53
Author: pepr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:21

Prosty trik, którego zwykle używam, to po prostu zawijanie go w listę:

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

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

(tak Wiem, że może to być niewygodne, ale czasami jest to na tyle proste, aby to zrobić.)

 39
Author: AmanicA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-08 10:39:48

(edit-Blair zaktualizował swoją niezwykle popularną odpowiedź, dzięki czemu jest teraz dokładna)

Myślę, że ważne jest, aby zauważyć, że obecny post z największą liczbą głosów (Blair Conrad), choć jest poprawny w odniesieniu do jego wyniku, jest mylący i jest borderline nieprawidłowe W oparciu o swoje definicje. Chociaż istnieje wiele języków (takich jak C), które pozwalają użytkownikowi przejść przez odniesienie lub przekazać przez wartość, Python nie jest jednym z nich.

Odpowiedź Davida Cournapeau wskazuje na prawdziwa odpowiedź i wyjaśnia, dlaczego zachowanie w poście Blair Conrad wydaje się być poprawne, podczas gdy definicje nie są.

W zakresie, w jakim Python jest przekazywana przez wartość, wszystkie języki są przekazywane przez wartość, ponieważ niektóre dane (czy to "wartość", czy "odniesienie") muszą zostać wysłane. Nie oznacza to jednak, że Python jest wartością przekazywaną w tym sensie, że programista C O tym pomyśli.

Jeśli chcesz zachowania, odpowiedź Blair Conrad jest w porządku. Ale jeśli chcesz wiedzieć, nakrętki i śruby o tym, dlaczego Python nie jest ani pass by value, ani pass by reference, przeczytaj odpowiedź Davida Cournapeau.

 34
Author: KobeJohn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-09 15:00:42

Nie ma zmiennych w Pythonie

Kluczem do zrozumienia przekazywania parametrów jest zaprzestanie myślenia o"zmiennych". Istnieją nazwy i obiekty w Pythonie i razem są wyglądają jak zmienne, ale warto zawsze rozróżniać te trzy.

  1. Python ma nazwy i Obiekty.
  2. przypisanie wiąże nazwę z obiektem.
  3. przekazanie argumentu do funkcji wiąże również nazwę (nazwę parametru funkcji) z obiekt.
To wszystko. Zmienność nie ma znaczenia dla tego pytania.

Przykład:

a = 1

Wiąże nazwę a z obiektem typu integer, który posiada wartość 1.

b = x

Wiąże nazwę b z tym samym obiektem, z którym nazwa x jest obecnie związana. Później nazwa b nie ma już nic wspólnego z nazwą x.

Patrz sekcje 3.1 oraz 4.2 w języku Python 3 Referencja.


Tak więc w kodzie pokazanym w pytaniu, polecenie self.Change(self.variable) wiąże nazwę var (w zakresie funkcji Change) do obiektu, który posiada wartość 'Original', a przypisanie var = 'Changed' (w ciele funkcji Change) przypisuje tę samą nazwę ponownie: do innego obiektu( który również posiada łańcuch znaków, ale mógł być czymś zupełnie innym).

 26
Author: Lutz Prechelt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-18 08:56:54

Masz tu naprawdę dobre odpowiedzi.

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' )
 22
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-27 04:28:48

W tym przypadku zmiennej o nazwie var w metodzie Change przypisane jest odniesienie do self.variable, a ty natychmiast przypisujesz łańcuch znaków do var. Nie wskazuje już na self.variable. Poniższy fragment kodu pokazuje, co się stanie, jeśli zmodyfikujesz strukturę danych wskazywaną przez var i self.variable, w tym przypadku listę:

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

Jestem pewien, że ktoś inny mógłby to wyjaśnić.

 16
Author: Mike Mazur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-12 10:39:59

Schemat pass-by-assignment Pythona nie jest taki sam jak opcja parametrów referencyjnych C++, ale okazuje się bardzo podobny do modelu przekazywania argumentów języka C (i innych) w praktyce:

  • niezmienne argumenty są skutecznie przekazywane " przez wartość."Obiekty takie jak liczby całkowite i ciągi znaków są przekazywane przez odniesienie do obiektu zamiast przez kopiowanie, ale ponieważ nie można zmienić niezmiennych obiektów w miejscu tak czy inaczej, efekt jest podobny do tworzenia przyjąłem.
  • zmienne argumenty są skutecznie przekazywane " przez wskaźnik."Obiekty takie jak listy i słowniki są również przekazywane przez odniesienie do obiektu, co jest podobne do sposobu C przekazuje tablice jako wskaźniki-zmienne obiekty mogą być zmieniane w miejscu w funkcji, podobnie jak tablice C.
 15
Author: ajkn1992,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-27 04:38:18

Jak możesz stwierdzić, musisz mieć zmienny obiekt, ale pozwól, że zasugeruję Ci sprawdzenie globalnych zmiennych, które mogą Ci pomóc lub nawet rozwiązać ten rodzaj problemu!

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

Przykład:

>>> 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
Author: Nuno Aniceto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-09 02:23:54

Wiele spostrzeżeń w odpowiedziach tutaj, ale myślę, że dodatkowy punkt nie jest wyraźnie wymienione tutaj wyraźnie. Cytowanie z dokumentacji Pythona https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python{[4]

" w Pythonie zmienne, do których odwołuje się tylko Funkcja, są w domyśle globalne. Jeśli zmiennej przypisana jest nowa wartość w dowolnym miejscu w ciele funkcji, przyjmuje się, że jest to wartość lokalna. Jeśli zmienna jest kiedykolwiek przypisując nową wartość wewnątrz funkcji, zmienna jest domyślnie lokalna i musisz jawnie zadeklarować ją jako "globalną". Choć na początku trochę zaskakujące, chwila zastanowienia wyjaśnia to. Z jednej strony, Wymaganie globalnego dla przypisanych zmiennych stanowi barierę przed niezamierzonymi skutkami ubocznymi. Z drugiej strony, gdyby global było wymagane dla wszystkich globalnych odniesień, używałbyś global cały czas. Musisz zadeklarować jako globalne każde odniesienie do wbudowanej funkcji lub komponentu zaimportowany moduł. Ten bałagan podważyłby przydatność globalnej deklaracji do identyfikacji skutków ubocznych."

Nawet gdy przekazujemy zmienny obiekt do funkcji, to nadal ma to zastosowanie. I dla mnie jasno wyjaśnia przyczynę różnicy w zachowaniu między przypisaniem do obiektu a działaniem na obiekcie w funkcji.

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)

Daje:

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

Przypisanie do zmiennej globalnej, która nie jest zadeklarowana jako globalna, powoduje utworzenie nowego obiektu lokalnego i przerywa łącze do oryginalnego obiektu.

 12
Author: Joop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-29 07:12:10

Oto proste (mam nadzieję) wyjaśnienie pojęcia pass by object używanego w Pythonie.
Za każdym razem, gdy przekazujesz obiekt do funkcji, sam obiekt jest przekazywany (obiekt w Pythonie jest właściwie tym, co można nazwać wartością w innych językach programowania), a nie odniesieniem do tego obiektu. Innymi słowy, gdy wywołujesz:

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

my_list = [0, 1]
change_me(my_list)

Przekazywany jest rzeczywisty obiekt - [0, 1] (który w innych językach programowania byłby nazywany wartością). Więc w rzeczywistości funkcja change_me będzie próbowała coś zrobić like:

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

Co oczywiście nie zmieni obiektu przekazanego do funkcji. Jeśli funkcja wyglądała tak:

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

Wtedy wywołanie spowoduje:

[0, 1].append(2)

Co oczywiście zmieni obiekt. Ta odpowiedź dobrze to wyjaśnia.

 8
Author: matino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:59

Pomijając wszystkie świetne wyjaśnienia, jak to działa w Pythonie, nie widzę prostej sugestii na ten problem. Jak wydaje się tworzyć obiekty i instancje, pythoniczny sposób obsługi zmiennych instancji i ich zmiany jest następujący:

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

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

W metodach instancji, Zwykle odwołujesz się do self, aby uzyskać dostęp do atrybutów instancji. Normalne jest ustawianie atrybutów instancji w __init__ i odczytywanie lub zmienianie ich w metodach instancji. Dlatego też zdasz self als pierwszą argument do def Change.

Innym rozwiązaniem byłoby stworzenie statycznej metody takiej jak ta:

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
 7
Author: Dolf Andringa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-09 02:23:27

Istnieje mała sztuczka, aby przekazać obiekt przez odniesienie, mimo że język nie umożliwia. Działa również w Javie, jest to lista z jednym elementem. ;-)

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
To brzydki hack, ale działa. ;- P
 6
Author: itmuckel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-21 16:47:42

Użyłem poniższej metody, aby szybko przekonwertować kilka kodów Fortran do Pythona. To prawda, że nie jest to przejście przez odniesienie, jak postawiono oryginalne pytanie, ale w niektórych przypadkach jest to prosta praca.

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
 4
Author: Brad Porter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-09 02:22:25

Podczas gdy pass by reference nie jest niczym, co dobrze pasuje do Pythona i powinno być rzadko używane, istnieją pewne obejścia, które mogą działać, aby uzyskać obiekt aktualnie przypisany do zmiennej lokalnej lub nawet ponownie przypisać zmienną lokalną z wnętrza wywołanej funkcji.

Podstawową ideą jest posiadanie funkcji, która może wykonać ten dostęp i może być przekazywana jako obiekt do innych funkcji lub przechowywana w klasie.

Jednym ze sposobów jest użycie global (dla zmiennych globalnych) lub nonlocal (dla zmiennych lokalnych zmienne w funkcji) w funkcji wrappera.

def change(wrapper):
    wrapper(7)

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

Ten sam pomysł działa dla odczytu i deletowania zmiennej.

Do zwykłego czytania istnieje jeszcze krótszy sposób użycia lambda: x, który zwraca funkcję wywołania, która po wywołaniu zwraca bieżącą wartość x. jest to nieco jak "wywoływanie po nazwie" używane w językach w odległej przeszłości.

Przekazywanie 3 wrapperów w celu uzyskania dostępu do zmiennej jest trochę nieporęczne, więc można je zawijać do klasy, która ma proxy atrybut:

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

Obsługa Pythons "reflection" umożliwia uzyskanie obiektu, który jest w stanie ponownie przypisać nazwę/zmienną w danym zakresie bez wyraźnego definiowania funkcji w tym zakresie:

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)

Tutaj klasa ByRef zawija dostęp do słownika. Tak więc dostęp do atrybutu wrapped jest tłumaczony na dostęp do elementu w przekazanym słowniku. Przekazując wynik wbudowanego locals i nazwę zmiennej lokalnej, kończy się uzyskaniem dostępu do zmiennej lokalnej. Pyton dokumentacja od 3.5 radzi, że zmiana słownika może nie zadziałać, ale wydaje mi się, że działa.

 3
Author: textshell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-20 14:02:37

Biorąc pod uwagę sposób, w jaki python obsługuje wartości i odniesienia do nich, jedynym sposobem, w jaki można odwoływać się do dowolnego atrybutu instancji jest nazwa:

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

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

W prawdziwym kodzie można oczywiście dodać sprawdzanie błędów w wyszukiwarce dict.

 3
Author: mARK bLOORE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-31 15:33:23

Ponieważ twój przykład jest zorientowany obiektowo, możesz wprowadzić następującą zmianę, aby osiągnąć podobny wynik:

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'
 2
Author: Jesse Hogan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-10 02:19:53

Możesz po prostu użyć pustej klasy jako instancji do przechowywania obiektów referencyjnych, ponieważ wewnętrzne atrybuty obiektów są przechowywane w słowniku instancji. Zobacz przykład.

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)
 0
Author: sergzach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-03 14:14:11