Übergabe einer Ganzzahl als Referenz in Python

94

Wie kann ich in Python eine Ganzzahl als Referenz übergeben?

Ich möchte den Wert einer Variablen ändern, die ich an die Funktion übergebe. Ich habe gelesen, dass alles in Python als Wert übergeben wird, aber es muss einen einfachen Trick geben. Zum Beispiel in Java könnten Sie die Referenztypen passieren Integer, Longusw.

  1. Wie kann ich eine Ganzzahl als Referenz an eine Funktion übergeben?
  2. Was sind die Best Practices?
CodeKingPlusPlus
quelle
Hier finden Sie eine nette, wenn auch leicht verschlungene Möglichkeit, Ihre Ints in eine anonyme Klasse (die veränderlich ist) zu verpacken, die sich wie eine 'Referenz' verhält : stackoverflow.com/a/1123054/409638 dh ref = type ('', () , {'n': 1})
Robert

Antworten:

105

In Python funktioniert das nicht ganz so. Python übergibt Verweise auf Objekte. Innerhalb Ihrer Funktion haben Sie ein Objekt - Sie können dieses Objekt (wenn möglich) mutieren. Ganzzahlen sind jedoch unveränderlich . Eine Problemumgehung besteht darin, die Ganzzahl in einem Container zu übergeben, der mutiert werden kann:

def change(x):
    x[0] = 3

x = [1]
change(x)
print x

Das ist bestenfalls hässlich / ungeschickt, aber in Python werden Sie es nicht besser machen. Der Grund dafür ist, dass Zuweisung ( =) in Python jedes Objekt nimmt, das das Ergebnis der rechten Seite ist, und es an alles bindet, was sich auf der linken Seite befindet * (oder es an die entsprechende Funktion übergibt).

Wenn wir dies verstehen, können wir sehen, warum es keine Möglichkeit gibt, den Wert eines unveränderlichen Objekts innerhalb einer Funktion zu ändern. Sie können keines seiner Attribute ändern, weil es unveränderlich ist, und Sie können der "Variablen" nicht einfach eine neue zuweisen Wert, weil Sie dann tatsächlich ein neues Objekt erstellen (das sich vom alten unterscheidet) und ihm den Namen geben, den das alte Objekt im lokalen Namespace hatte.

Normalerweise ist die Abhilfe einfach zurückgeben das Objekt , das Sie wollen:

def multiply_by_2(x):
    return 2*x

x = 1
x = multiply_by_2(x)

* Im ersten Beispielfall oben wird 3tatsächlich an übergeben x.__setitem__.

mgilson
quelle
11
"Python übergibt Objekte." Python übergibt Referenzen (Zeiger auf Objekte).
user102008
6
@ user102008 - Fairer Punkt. Die Semantik an diesem Punkt wird sehr schlammig, wie ich fühle. Letztendlich sind sie auch keine "Zeiger". Zumindest sicherlich nicht im gleichen Sinne wie Sie C. (Sie müssen zum Beispiel nicht dereferenziert werden). In meinem mentalen Modell ist es einfacher, sich Dinge als "Namen" und "Objekte" vorzustellen. Die Zuweisung bindet einen oder mehrere "Namen" links an einen oder mehrere "Objekte" rechts. Wenn Sie eine Funktion aufrufen, übergeben Sie das "Objekt" (binden es effektiv an einen neuen lokalen Namen innerhalb der Funktion).
mgilson
Dies ist natürlich eine Beschreibung der Python- Referenzen , aber es ist nicht einfach, eine kurze Beschreibung für diejenigen zu finden, die mit der Terminologie nicht vertraut sind.
mgilson
2
Kein Wunder, dass wir mit der Terminologie verwechselt werden. Hier haben wir es als Call-by-Object beschrieben und hier wird es als Pass-by-Value beschrieben . An anderer Stelle heißt es "Pass-by-Reference" mit einem Sternchen, was das eigentlich bedeutet ... Grundsätzlich besteht das Problem darin, dass die Community nicht herausgefunden hat, wie sie es nennen soll
mgilson
1
Python-Referenzen sind genau die gleichen wie Java-Referenzen. Und Java-Referenzen entsprechen den JLS-Zeigern auf Objekte. Grundsätzlich gibt es eine vollständige Bijektion zwischen Java / Python-Referenzen und C ++ - Zeigern auf Objekte in der Semantik, und nichts anderes beschreibt diese Semantik ebenfalls. "Sie müssen nicht dereferenziert werden" Nun, der .Bediener dereferenziert einfach die linke Seite. Operatoren können in verschiedenen Sprachen unterschiedlich sein.
user102008
30

In den meisten Fällen, in denen Sie als Referenz übergeben müssten, müssen Sie mehr als einen Wert an den Anrufer zurückgeben. Eine "Best Practice" ist die Verwendung mehrerer Rückgabewerte, was in Python viel einfacher ist als in Sprachen wie Java.

Hier ist ein einfaches Beispiel:

def RectToPolar(x, y):
    r = (x ** 2 + y ** 2) ** 0.5
    theta = math.atan2(y, x)
    return r, theta # return 2 things at once

r, theta = RectToPolar(3, 4) # assign 2 things at once
Gabe
quelle
9

Einen Wert nicht genau direkt übergeben, sondern so verwenden, als ob er übergeben worden wäre.

x = 7
def my_method():
    nonlocal x
    x += 1
my_method()
print(x) # 8

Vorsichtsmaßnahmen:

  • nonlocal wurde in Python 3 eingeführt
  • Wenn der einschließende Bereich der globale ist, verwenden Sie globalanstelle von nonlocal.
Uri
quelle
7

Die beste Vorgehensweise besteht darin, einen Schritt zurückzutreten und zu fragen, ob Sie dies wirklich tun müssen. Warum möchten Sie den Wert einer Variablen ändern, die Sie an die Funktion übergeben?

Wenn Sie es für einen schnellen Hack tun müssen, ist der schnellste Weg, eine listGanzzahl zu halten und [0]bei jeder Verwendung eine Ganzzahl zu verwenden, wie die Antwort von mgilson zeigt.

Wenn Sie es für etwas größere Bedeutung zu tun brauchen, schreiben Sie ein , classdie eine hat intals Attribut, so dass Sie es einfach einstellen. Dies zwingt Sie natürlich dazu, einen guten Namen für die Klasse und das Attribut zu finden. Wenn Ihnen nichts einfällt, lesen Sie den Satz einige Male erneut und verwenden Sie dann den list.

Wenn Sie versuchen, eine Java-Sprache direkt nach Python zu portieren, machen Sie das im Allgemeinen falsch. Selbst wenn etwas direkt korrespondiert (wie bei static/ @staticmethod), möchten Sie es in den meisten Python-Programmen nicht verwenden, nur weil Sie es in Java verwenden würden.

abarnert
quelle
JAVA übergibt keine Ganzzahlen als Referenz (Ganzzahlen oder Objekte. Boxobjekte werden bei der Zuweisung ebenfalls ersetzt).
Luis Masuelli
@LuisMasuelli Nun, Box-Primitive werden wie Objekte behandelt. Das einzige, was ihre Verwendung verhindert, wie es das OP will, ist die Tatsache, dass Boxen unveränderlich sind (und da die Primitive selbst auch unveränderlich sind, ist das Ganze unveränderlich und kann nur geändert werden variable Ebene)
Kroltan
Ein guter Anwendungsfall hierfür ist das Zählen einer Anzahl von Aufrufen (insgesamt, nicht Tiefe) innerhalb einer rekursiven Funktion. Sie benötigen eine Nummer, die in allen verzweigten Anrufen erhöht werden kann. Ein Standard-Int schneidet es einfach nicht
z33k
4

In Python ist jeder Wert eine Referenz (ein Zeiger auf ein Objekt), genau wie bei Nicht-Grundelementen in Java. Ebenso wie Python hat Python nur einen Wert für die Übergabe. Semantisch gesehen sind sie also ziemlich gleich.

Da Sie Java in Ihrer Frage erwähnen, würde ich gerne sehen, wie Sie das erreichen, was Sie in Java wollen. Wenn Sie es in Java zeigen können, kann ich Ihnen zeigen, wie es in Python genau gleichwertig gemacht wird.

newacct
quelle
4

Ein numpy Einzelelement-Array ist veränderlich und kann dennoch für die meisten Zwecke ausgewertet werden, als wäre es eine numerische Python-Variable. Daher ist es ein bequemerer Container für Referenznummern als eine Liste mit einzelnen Elementen.

    import numpy as np
    def triple_var_by_ref(x):
        x[0]=x[0]*3
    a=np.array([2])
    triple_var_by_ref(a)
    print(a+1)

Ausgabe:

3
Trisoloriansunscreen
quelle
3

Vielleicht ist es nicht pythonisch, aber Sie können dies tun

import ctypes

def incr(a):
    a += 1

x = ctypes.c_int(1) # create c-var
incr(ctypes.ctypes.byref(x)) # passing by ref
Sergey Kovalev
quelle
Mann, der schlau ist.
Xilpex
2
class PassByReference:
    def Change(self, var):
        self.a = var
        print(self.a)
s=PassByReference()
s.Change(5)     
Shruti
quelle
2

Vielleicht etwas selbstdokumentierender als der Trick mit der Liste der Länge 1 ist der alte Trick mit dem leeren Typ:

def inc_i(v):
    v.i += 1

x = type('', (), {})()
x.i = 7
inc_i(x)
print(x.i)
Mheyman
quelle
Oder wickeln Sie die Nummer in eine Klasse: github.com/markrages/python_mutable_number
markrages
0

In Python wird alles als Wert übergeben. Wenn Sie jedoch einen Status ändern möchten, können Sie den Wert einer Ganzzahl in einer Liste oder einem Objekt ändern, das an eine Methode übergeben wird.

rekursiv
quelle
3
Ich denke, weil everything is passed by valuedas nicht wirklich stimmt. Zitat docs:arguments are passed using call by value (where the value is always an object reference, not the value of the object)
Astery
1
Listen, Objekte und Wörterbücher werden als Referenz übergeben
Mashakal
2
Wenn dies wahr wäre, würde die Zuweisung zu einem Parameter in einer Funktion an der Aufrufstelle wiedergegeben. Wie Astery zitiert, erfolgt die Übergabe nach Wert und diese Werte sind Objektreferenzen.
rekursiv