Hoe geef ik een variabele door als referentie?

De Python-documentatie lijkt onduidelijk of parameters worden doorgegeven door middel van referentie of waarde, en de volgende code produceert de ongewijzigde waarde ‘Origineel’

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

Is er iets dat ik kan doen om de variabele door te geven door middel van echte referentie?


Antwoord 1, autoriteit 100%

Argumenten zijn geslaagd door opdracht. De grondgedachte hierachter is tweeledig:

  1. de opgegeven parameter is eigenlijk een verwijzingnaar een object (maar de verwijzing wordt doorgegeven door waarde)
  2. sommige gegevenstypen kunnen worden gewijzigd, maar andere niet

Dus:

  • Als u een veranderbaarobject doorgeeft aan een methode, krijgt de methode een verwijzing naar datzelfde object en kunt u het naar hartelust muteren, maar als u de verwijzing opnieuw bindt in de methode, zal de buitenste reikwijdte er niets van weten, en nadat u klaar bent, zal de buitenste referentie nog steeds naar het oorspronkelijke object wijzen.

  • Als je een onveranderlijkobject doorgeeft aan een methode, kun je de buitenste referentie nog steeds niet opnieuw binden, en je kunt het object zelfs niet muteren.

Laten we enkele voorbeelden geven om het nog duidelijker te maken.

Lijst – een veranderlijk type

Laten we proberen de lijst aan te passen die aan een methode is doorgegeven:

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)

Uitvoer:

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

Aangezien de ingevoerde parameter een verwijzing is naar outer_list, en geen kopie ervan, kunnen we de mutatielijstmethoden gebruiken om deze te wijzigen en de wijzigingen in de buitenste scope weer te geven.

Laten we nu eens kijken wat er gebeurt als we proberen de referentie te wijzigen die als parameter is doorgegeven:

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)

Uitvoer:

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

Aangezien de parameter the_listper waarde werd doorgegeven, had het toewijzen van een nieuwe lijst geen effect dat de code buiten de methode kon zien. De the_listwas een kopie van de outer_listreferentie, en we hadden the_listnaar een nieuwe lijst laten verwijzen, maar er was geen manier om te veranderen waar outer_listwees.

String – een onveranderlijk type

Het is onveranderlijk, dus we kunnen niets doen om de inhoud van de tekenreeks te veranderen

Laten we nu proberen de referentie te wijzigen

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)

Uitvoer:

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

Nogmaals, aangezien de parameter the_stringper waarde werd doorgegeven, had het toewijzen van een nieuwe tekenreeks geen effect dat de code buiten de methode kon zien. De the_stringwas een kopie van de outer_stringreferentie, en we hadden the_stringnaar een nieuwe string laten wijzen, maar er was geen manier om te veranderen waar outer_stringwees.

Ik hoop dat dit de zaken een beetje opheldert.

BEWERK:Er is opgemerkt dat dit geen antwoord geeft op de vraag die @David oorspronkelijk stelde: “Is er iets dat ik kan doen om de variabele door te geven door daadwerkelijke verwijzing?”. Laten we daaraan werken.

Hoe kunnen we dit omzeilen?

Zoals het antwoord van @Andrea laat zien, zou je de nieuwe waarde kunnen retourneren. Dit verandert niets aan de manier waarop dingen worden doorgegeven, maar geeft je wel de informatie die je terug wilt hebben:

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)

Als u het gebruik van een retourwaarde echt wilt vermijden, kunt u een klasse maken om uw waarde vast te houden en deze aan de functie door te geven of een bestaande klasse gebruiken, zoals een lijst:

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

Hoewel dit een beetje omslachtig lijkt.


Antwoord 2, autoriteit 25%

Het probleem komt voort uit een verkeerd begrip van wat variabelen in Python zijn. Als je de meeste traditionele talen gewend bent, heb je een mentaal model van wat er in de volgende volgorde gebeurt:

a = 1
a = 2

U denkt dat aeen geheugenlocatie is die de waarde 1opslaat en vervolgens wordt bijgewerkt om de waarde 2op te slaan. Dat is niet hoe de dingen werken in Python. In plaats daarvan begint aals een verwijzing naar een object met de waarde 1en wordt vervolgens opnieuw toegewezen als een verwijzing naar een object met de waarde 2. Die twee objecten kunnen naast elkaar blijven bestaan, ook al verwijst aniet meer naar de eerste; in feite kunnen ze worden gedeeld door een willekeurig aantal andere referenties binnen het programma.

Als je een functie aanroept met een parameter, wordt er een nieuwe referentie gemaakt die verwijst naar het doorgegeven object. Dit staat los van de referentie die werd gebruikt in de functieaanroep, dus er is geen manier om die referentie bij te werken en te maken verwijzen naar een nieuw object. In jouw voorbeeld:

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

self.variableis een verwijzing naar het stringobject 'Original'. Wanneer je Changeaanroept, creëer je een tweede referentie varnaar het object. Binnen de functie wijst u de verwijzing varopnieuw toe aan een ander tekenreeksobject 'Changed', maar de verwijzing self.variableis apart en verandert niet .

De enige manier om dit te omzeilen is door een veranderlijk object door te geven. Omdat beide verwijzingen naar hetzelfde object verwijzen, worden eventuele wijzigingen aan het object op beide plaatsen weergegeven.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)
def Change(self, var):
    var[0] = 'Changed'

Antwoord 3, autoriteit 12%

Ik vond de andere antwoorden nogal lang en ingewikkeld, dus ik heb dit eenvoudige diagram gemaakt om uit te leggen hoe Python variabelen en parameters behandelt.
voer hier de afbeeldingsbeschrijving in


Antwoord 4, autoriteit 8%

Het is geen pass-by-waarde of pass-by-referentie – het is call-by-object. Zie dit, door Fredrik Lundh:

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

Hier is een belangrijk citaat:

“…variabelen [namen] zijn geenobjecten; ze kunnen niet worden aangegeven door andere variabelen of waarnaar wordt verwezen door objecten.”

In uw voorbeeld, wanneer de methode Changewordt aangeroepen–a namespaceis ervoor gemaakt; en varwordt een naam, binnen die naamruimte, voor het tekenreeksobject 'Original'. Dat object heeft dan een naam in twee namespaces. Vervolgens bindt var = 'Changed'varaan een nieuw tekenreeksobject, en dus vergeet de naamruimte van de methode 'Original'. Ten slotte wordt die naamruimte vergeten, en de string 'Changed'erbij.


Antwoord 5, autoriteit 6%

Denk aan dingen die door toewijzingworden doorgegeven in plaats van door verwijzing/door waarde. Zo is het altijd duidelijk wat er gebeurt, zolang je maar begrijpt wat er gebeurt tijdens de normale opdracht.

Dus, wanneer een lijst wordt doorgegeven aan een functie/methode, wordt de lijst toegewezen aan de parameternaam. Toevoegen aan de lijst zal ertoe leiden dat de lijst wordt gewijzigd. Het opnieuw toewijzen van de lijst binnenzal de originele lijst niet veranderen, aangezien:

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

Omdat onveranderlijke typen niet kunnen worden gewijzigd, lijkenze te worden doorgegeven door waarde – een int doorgeven aan een functie betekent dat de int wordt toegewezen aan de parameter van de functie. Je kunt dat alleen maar opnieuw toewijzen, maar het verandert de oorspronkelijke waarde van de variabelen niet.


Antwoord 6, autoriteit 3%

Er zijn geen variabelen in Python

De sleutel tot het begrijpen van het doorgeven van parameters is om te stoppen met denken aan ‘variabelen’. Er zijn namen en objecten in Python en samen zijn ze
verschijnen als variabelen, maar het is handig om altijd de drie te onderscheiden.

  1. Python heeft namen en objecten.
  2. Toewijzing bindt een naam aan een object.
  3. Het doorgeven van een argument aan een functie bindt ook een naam (de parameternaam van de functie) aan een object.

Dat is alles. Veranderlijkheid is niet relevant voor deze vraag.

Voorbeeld:

a = 1

Dit bindt de naam aaan een object van het type integer met de waarde 1.

b = x

Dit bindt de naam baan hetzelfde object waaraan de naam xmomenteel is gebonden.
Daarna heeft de naam bniets meer te maken met de naam x.

Zie secties 3.1en 4.2in de Python 3-taalreferentie.

Hoe het voorbeeld in de vraag te lezen

In de code die in de vraag wordt getoond, bindt de instructie self.Change(self.variable)de naam var(in het kader van functie Change) naar het object met de waarde 'Original'en de toewijzing var = 'Changed'(in de hoofdtekst van functie Change) wijst dezelfde naam opnieuw toe: aan een ander object (dat toevallig ook een string bevat, maar het zou iets heel anders kunnen zijn).

Hoe passeert u een referentie

Dus als het ding dat je wilt veranderen een veranderlijk object is, is er geen probleem, omdat alles effectief door middel van referentie wordt doorgegeven.

Als het een onveranderlijkobject is (bijv. een bool, getal, string), de manier om te gaan is om het in een veranderlijk object te wikkelen.
De snelle en vuile oplossing hiervoor is een lijst met één element (in plaats van self.variable, geef [self.variable]door en in de functie wijzig var[0]).
De meer pythonischebenadering zou zijn om een ​​triviale klasse met één kenmerk te introduceren. De functie ontvangt een instantie van de klasse en manipuleert het attribuut.


Antwoord 7, autoriteit 2%

Effbot (ook bekend als Fredrik Lundh) heeft de variabele doorgavestijl van Python beschreven als call-by-object: http:// effbot.org/zone/call-by-object.htm

Objecten worden op de heap toegewezen en verwijzingen ernaar kunnen overal worden doorgegeven.

  • Als je een toewijzing maakt zoals x = 1000, wordt er een woordenboekitem gemaakt dat de tekenreeks “x” in de huidige naamruimte toewijst aan een pointer naar het integer-object dat duizend bevat .

  • Als u “x” bijwerkt met x = 2000, wordt een nieuw geheel getal gemaakt en wordt de woordenlijst bijgewerkt om naar het nieuwe object te wijzen. Het oude duizend-object is ongewijzigd (en kan al dan niet levend zijn, afhankelijk van of iets anders naar het object verwijst).

  • Als je een nieuwe opdracht doet, zoals y = x, wordt er een nieuw woordenboekitem “y” gemaakt dat verwijst naar hetzelfde object als het item voor “x”.

  • Objecten zoals strings en integers zijn onveranderlijk. Dit betekent simpelweg dat er geen methoden zijn die het object kunnen wijzigen nadat het is gemaakt. Als het integer-object één-duizend bijvoorbeeld is gemaakt, zal het nooit meer veranderen. Wiskunde wordt gedaan door nieuwe integer-objecten te maken.

  • Objecten zoals lijsten zijn veranderbaar. Dit betekent dat de inhoud van het object kan worden gewijzigd door alles wat naar het object wijst. Bijvoorbeeld x = []; y = x; x.append(10); print yzal [10]afdrukken. De lege lijst is gemaakt. Zowel “x” als “y” verwijzen naar dezelfde lijst. De methode appendmuteert (werkt bij) het lijstobject (zoals het toevoegen van een record aan een database) en het resultaat is zichtbaar voor zowel “x” als “y” (net zoals een database-update zichtbaar zou zijn voor elke verbinding met die database).

Ik hoop dat dit het probleem voor je verduidelijkt.


Antwoord 8, autoriteit 2%

Technisch gezien gebruikt Python altijd pass-by-referentiewaarden. Ik herhaal mijn andere antwoordom mijn stelling te ondersteunen.

Python gebruikt altijd pass-by-referentiewaarden. Er is geen uitzondering. Elke variabele toewijzing betekent het kopiëren van de referentiewaarde. Geen uitzondering. Elke variabele is de naam die is gekoppeld aan de referentiewaarde. Altijd.

U kunt een referentiewaarde beschouwen als het adres van het doelobject. Het adres wordt automatisch verwijderd wanneer het wordt gebruikt. Op deze manier lijkt het alsof u, werkend met de referentiewaarde, rechtstreeks met het doelobject werkt. Maar er zit altijd een referentie tussen, een stap meer om naar het doel te springen.

Hier is het voorbeeld dat bewijst dat Python pass by reference gebruikt:

geïllustreerd voorbeeld van het doorgeven van het argument

Als het argument door waarde is doorgegeven, kan de buitenste lstniet worden gewijzigd. De groene zijn de doelobjecten (de zwarte is de waarde die erin is opgeslagen, de rode is het objecttype), de gele is het geheugen met de referentiewaarde erin – getekend als de pijl. De blauwe ononderbroken pijl is de referentiewaarde die aan de functie is doorgegeven (via het gestreepte blauwe pijlpad). Het lelijke donkergeel is het interne woordenboek. (Het zou eigenlijk ook als een groene ellips kunnen worden getekend. De kleur en de vorm zeggen alleen dat het inwendig is.)

U kunt de id()ingebouwde functie om te leren wat de referentiewaarde is (dat wil zeggen, het adres van het doelobject).

In gecompileerde talen is een variabele een geheugenruimte die de waarde van het type kan vastleggen. In Python is een variabele een naam (intern vastgelegd als een string) gebonden aan de referentievariabele die de referentiewaarde voor het doelobject bevat. De naam van de variabele is de sleutel in het interne woordenboek, het waardegedeelte van dat woordenboekitem slaat de referentiewaarde op naar het doel.

Referentiewaarden zijn verborgen in Python. Er is geen expliciet gebruikerstype voor het opslaan van de referentiewaarde. U kunt echter een lijstelement (of element in een ander geschikt containertype) als referentievariabele gebruiken, omdat alle containers de elementen ook opslaan als verwijzingen naar de doelobjecten. Met andere woorden, elementen zitten eigenlijk niet in de container — alleen de verwijzingen naar elementen wel.


Antwoord 9, autoriteit 2%

Een simpele truc die ik normaal gesproken gebruik is om het gewoon in een lijst te plaatsen:

def Change(self, var):
    var[0] = 'Changed'
variable = ['Original']
self.Change(variable)      
print variable[0]

(Ja, ik weet dat dit onhandig kan zijn, maar soms is het eenvoudig genoeg om dit te doen.)


Antwoord 10

(edit – Blair heeft zijn enorm populaire antwoord bijgewerkt, zodat het nu juist is)

Ik denk dat het belangrijk is op te merken dat het huidige bericht met de meeste stemmen (door Blair Conrad), hoewel het correct is met betrekking tot het resultaat, misleidend is en op basis van de definities op het randje van onjuist is. Hoewel er veel talen zijn (zoals C) waarmee de gebruiker een referentie of een waarde kan doorgeven, is Python daar niet een van.

Het antwoord van David Cournapeau verwijst naar het echte antwoord en legt uit waarom het gedrag in de post van Blair Conrad correct lijkt, terwijl de definities dat niet zijn.

In de mate dat Python een waarde passeert, worden alle talen door een waarde doorgegeven, aangezien een bepaald stuk gegevens (of het nu een “waarde” of een “referentie”) is, moet worden verzonden. Dat betekent echter niet dat Python waardeloos is in de zin dat een C-programmeur het zou bedenken.

Als je het gedrag wilt, is het antwoord van Blair Conrad prima. Maar als je de moeren en bouten wilt weten waarom Python geen pass-by-waarde of pass-by-referentie is, lees dan het antwoord van David Cournapeau.


Antwoord 11

Je hebt hier heel goede antwoorden.

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

Antwoord 12

Python’s pass-by-toewijzingsschema is niet helemaal hetzelfde als de optie voor referentieparameters van C++, maar het blijkt in de praktijk erg te lijken op het argument-passing-model van de C-taal (en andere) :

  • Onveranderlijke argumenten worden effectief doorgegeven op waarde. Objecten zoals gehele getallen en strings worden doorgegeven door objectreferentie in plaats van door te kopiëren, maar omdat u onveranderlijke objecten toch niet op hun plaats kunt veranderen, effect lijkt veel op het maken van een kopie.
  • Veranderlijke argumenten worden effectief doorgegeven per aanwijzer. Objecten zoals lijsten
    en woordenboeken worden ook doorgegeven door objectreferentie, wat vergelijkbaar is met de manier waarop C
    geeft arrays door als aanwijzers – veranderlijke objecten kunnen op hun plaats in de functie worden gewijzigd,
    net als C-arrays.

Antwoord 13

In dit geval wordt aan de variabele met de titel varin de methode Changeeen verwijzing naar self.variabletoegewezen en wijst u onmiddellijk een string toe naar var. Het verwijst niet langer naar self.variable. Het volgende codefragment laat zien wat er zou gebeuren als u de gegevensstructuur wijzigt waarnaar wordt verwezen door varen self.variable, in dit geval een lijst:

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

Ik weet zeker dat iemand anders dit verder kan verduidelijken.


Antwoord 14

Zoals je kunt aangeven, moet je een veranderlijk object hebben, maar laat me je aanraden om de globale variabelen te controleren, omdat ze je kunnen helpen of zelfs dit soort problemen kunnen oplossen!

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

voorbeeld:

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

Antwoord 15

Veel inzichten in antwoorden hier, maar ik denk dat een extra punt hier niet duidelijk expliciet wordt genoemd. Citeren uit python-documentatie https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

“In Python zijn variabelen waarnaar alleen binnen een functie wordt verwezen impliciet globaal. Als aan een variabele ergens in de hoofdtekst van de functie een nieuwe waarde wordt toegewezen, wordt aangenomen dat het een lokale waarde is. Als er ooit een variabele wordt toegewezen een nieuwe waarde binnen de functie is, is de variabele impliciet lokaal en moet u deze expliciet als ‘globaal’ declareren.
Hoewel in het begin een beetje verrassend, wordt dit door een moment van overweging verklaard. Aan de ene kant biedt het vereisen van globaal voor toegewezen variabelen een barrière tegen onbedoelde bijwerkingen. Aan de andere kant, als globaal was vereist voor alle globale referenties, zou u de hele tijd globaal gebruiken. U moet elke verwijzing naar een ingebouwde functie of naar een component van een geïmporteerde module als globaal declareren. Deze rommel zou het nut van de globale verklaring voor het identificeren van bijwerkingen tenietdoen.”

Zelfs bij het doorgeven van een veranderlijk object aan een functie is dit nog steeds van toepassing. En legt voor mij duidelijk de reden uit voor het verschil in gedrag tussen het toewijzen aan het object en het bedienen van het object in de functie.

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)

geeft:

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

De toewijzing aan een globale variabele die niet globaal is gedeclareerd, creëert daarom een ​​nieuw lokaal object en verbreekt de link naar het oorspronkelijke object.


Antwoord 16

Hier is de eenvoudige (hoop ik) uitleg van het concept pass by objectdat in Python wordt gebruikt.
Telkens wanneer je een object doorgeeft aan de functie, wordt het object zelf doorgegeven (object in Python is eigenlijk wat je een waarde zou noemen in andere programmeertalen) en niet de verwijzing naar dit object. Met andere woorden, wanneer u belt:

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

Het werkelijke object – [0, 1] (dat in andere programmeertalen een waarde zou worden genoemd) wordt doorgegeven. Dus in feite zal de functie change_meiets proberen te doen als:

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

wat uiteraard het object dat aan de functie is doorgegeven niet zal veranderen. Als de functie er zo uitzag:

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

Dan zou de oproep resulteren in:

[0, 1].append(2)

wat uiteraard het object zal veranderen. Dit antwoordlegt het goed uit.


Antwoord 17

Afgezien van alle geweldige uitleg over hoe dit spul in Python werkt, zie ik geen eenvoudige suggestie voor het probleem. Zoals het lijkt alsof je objecten en instanties maakt, is de pythonische manier om instantievariabelen te verwerken en te wijzigen de volgende:

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

Bij instantiemethoden verwijst u normaal gesproken naar selfom toegang te krijgen tot instantiekenmerken. Het is normaal om instantiekenmerken in __init__in te stellen en deze in instantiemethoden te lezen of te wijzigen. Dat is ook de reden waarom je selfals eerste argument doorgeeft aan def Change.

Een andere oplossing zou zijn om een ​​statische methode als deze te maken:

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

Antwoord 18

Er is een kleine truc om een ​​object door te verwijzen als referentie, ook al maakt de taal dit niet mogelijk. Het werkt ook in Java, het is de lijst met één item. 😉

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

Het is een lelijke hack, maar het werkt. ;-P


Antwoord 19

Ik heb de volgende methode gebruikt om snel een aantal Fortran-codes naar Python te converteren. Toegegeven, het is geen verwijzing naar de oorspronkelijke vraag, maar in sommige gevallen is het een eenvoudige omweg.

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

Antwoord 20

gezien de manier waarop python omgaat met waarden en verwijzingen ernaar, is de enige manier waarop u naar een willekeurig instantiekenmerk kunt verwijzen, op naam:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable
    def change(self, var):
        self.__dict__[var] = 'Changed'

in echte code zou je natuurlijk foutcontrole toevoegen bij het opzoeken van het dictaat.


Antwoord 21

Aangezien woordenboeken door middel van verwijzing worden doorgegeven, kunt u een dict-variabele gebruiken om alle referentiewaarden erin op te slaan.

# 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!"
    return result
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 returned value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

Antwoord 22

Aangezien uw voorbeeld objectgeoriënteerd is, kunt u de volgende wijziging aanbrengen om een ​​vergelijkbaar resultaat te bereiken:

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'

Antwoord 23

Omdat het nergens lijkt te worden genoemd, is een benadering om referenties te simuleren zoals bekend van b.v. C++ is om een ​​”update” -functie te gebruiken en die door te geven in plaats van de eigenlijke variabele (of liever “naam”):

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

Dit is vooral handig voor “out-only references” of in een situatie met meerdere threads / processen (door de update-functie thread / multiprocessing veilig te maken).

Uiteraard staat het bovenstaande niet toe om de waarde te lezen, alleen bij te werken.


Antwoord 24

Pass-By-Reference in Python is heel anders dan het concept van pass by reference in C++/Java.

  • Java&C#:primitieve typen (inclusief string) doorgeven door waarde (kopie), referentietype wordt doorgegeven door verwijzing (adreskopie), zodat alle wijzigingen die in de parameter in de aangeroepen functie zijn aangebracht, zichtbaar zijn naar de beller.
  • C++:Zowel pass-by-referentie als pass-by-waarde zijn toegestaan. Als een parameter door verwijzing wordt doorgegeven, kunt u deze wijzigen of niet, afhankelijk van of de parameter als const is doorgegeven of niet. Echter, const of niet, de parameter behoudt de referentie naar het object en referentie kan niet worden toegewezen om te verwijzen naar een ander object binnen de aangeroepen functie.
  • Python:
    Python is pass-by-object-reference , waarvan vaak wordt gezegd: Object references are pass by value. [Lees hier]1. Zowel de aanroeper als de functie verwijzen naar hetzelfde object, maar de parameter in de functie is een nieuwe variabele die slechts een kopie van het object in de aanroeper vasthoudt. Net als C++ kan een parameter al dan niet in functie worden gewijzigd – Dit hangt af van het type object dat wordt doorgegeven. bv; Een onveranderlijk objecttype kan niet worden gewijzigd in de aangeroepen functie, terwijl een veranderlijk object kan worden bijgewerkt of opnieuw geïnitialiseerd. Een cruciaal verschil tussen het bijwerken of opnieuw toewijzen/opnieuw initialiseren van de veranderlijke variabele is dat de bijgewerkte waarde wordt weerspiegeld in de aangeroepen functie, terwijl de opnieuw geïnitialiseerde waarde dat niet doet. De reikwijdte van elke toewijzing van een nieuw object aan een veranderlijke variabele is lokaal voor de functie in de python. Voorbeelden van @blair-conrad zijn geweldig om dit te begrijpen.

Antwoord 25

Hoewel pass-by-referentie niets is dat goed in Python past en zelden zou moeten worden gebruikt, zijn er enkele tijdelijke oplossingen die kunnen werken om het object dat momenteel is toegewezen aan een lokale variabele of zelfs een lokale variabele opnieuw toe te wijzen vanuit een aangeroepen functie.

Het basisidee is om een ​​functie te hebben die die toegang kan geven en die als object kan worden doorgegeven aan andere functies of kan worden opgeslagen in een klasse.

Een manier is om global(voor globale variabelen) of nonlocal(voor lokale variabelen in een functie) te gebruiken in een wrapper-functie.

def change(wrapper):
    wrapper(7)
x = 5
def setter(val):
    global x
    x = val
print(x)

Hetzelfde idee werkt voor het lezen en delinstellen van een variabele.

Voor alleen lezen is er zelfs een kortere manier om gewoon lambda: xte gebruiken die een aanroepbare waarde retourneert die bij aanroep de huidige waarde van x retourneert. Dit lijkt een beetje op “call by name” dat in het verre verleden in talen werd gebruikt.

3 wrappers doorgeven om toegang te krijgen tot een variabele is een beetje onpraktisch, dus die kunnen worden ingepakt in een klasse die een proxy-attribuut heeft:

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 “reflectie”-ondersteuning maakt het mogelijk om een ​​object te krijgen dat in staat is om een ​​naam/variabele opnieuw toe te wijzen in een bepaald bereik zonder expliciet functies in dat bereik te definiëren:

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)

Hier verpakt de klasse ByRefeen woordenboektoegang. Dus attribuuttoegang tot wrappedwordt vertaald naar een itemtoegang in het doorgegeven woordenboek. Door het resultaat van de ingebouwde localsen de naam van een lokale variabele door te geven, krijgt dit uiteindelijk toegang tot een lokale variabele. De Python-documentatie vanaf 3.5 geeft aan dat het wijzigen van het woordenboek misschien niet werkt, maar het lijkt voor mij te werken.


Antwoord 26

U kunt alleen een lege klassegebruiken als een instantie om referentieobjecten op te slaan, omdat interne objectkenmerken worden opgeslagen in een instantiewoordenboek. Zie het voorbeeld.

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)

Antwoord 27

Ik ben nieuw bij Python, ben gisteren begonnen (hoewel ik al 45 jaar programmeer).

Ik kwam hier omdat ik een functie aan het schrijven was waarbij ik twee zogenaamde out-parameters wilde hebben. Als het maar één out-parameter zou zijn geweest, zou ik nu niet vastlopen bij het controleren hoe referentie / waarde werkt in Python. Ik zou in plaats daarvan gewoon de retourwaarde van de functie hebben gebruikt. Maar aangezien ik tweevan dergelijke out-parameters nodig had, voelde ik dat ik het moest regelen.

In dit bericht ga ik laten zien hoe ik mijn situatie heb opgelost. Misschien vinden anderen die hier komen het waardevol, ook al is het niet echt een antwoord op de onderwerpvraag. Ervaren Python-programmeurs kennen natuurlijk al de oplossing die ik gebruikte, maar het was nieuw voor mij.

Uit de antwoorden hier kon ik snel zien dat Python in dit opzicht een beetje op Javascript werkt en dat je tijdelijke oplossingen moet gebruiken als je de referentiefunctionaliteit wilt.

Maar toen vond ik iets leuks in Python waarvan ik denk dat ik het nog niet eerder in andere talen heb gezien, namelijk dat je meer dan één waarde uit een functie kunt retourneren, op een eenvoudige door komma’s gescheiden manier, zoals deze:

p>

def somefunction(p):
    a=p+1
    b=p+2
    c=-p
    return a, b, c

en dat je dat aan de bellende kant op dezelfde manier kunt afhandelen, zoals dit

x, y, z = somefunction(w)

Dat was goed genoeg voor mij en ik was tevreden. Het is niet nodig om een ​​tijdelijke oplossing te gebruiken.

In andere talen kun je natuurlijk ook veel waarden retourneren, maar dan meestal in de from van een object, en je moet de aanroepende kant dienovereenkomstig aanpassen.

De Python-manier om het te doen was leuk en eenvoudig.

Als je door verwijzingnog meer wilt nabootsen, kun je het volgende doen:

def somefunction(a, b, c):
    a = a * 2
    b = b + a
    c = a * b * c
    return a, b, c
x = 3
y = 5
z = 10
print(F"Before : {x}, {y}, {z}")
x, y, z = somefunction(x, y, z)
print(F"After  : {x}, {y}, {z}")

wat dit resultaat geeft

Vóór: 3, 5, 10
Na: 6, 11, 660

Antwoord 28

u kunt ook ctypes gebruiken die er ongeveer zo uit zouden zien

import ctypes
def f(a):
    a.value=2398 ## resign the value in a function
a = ctypes.c_int(0)
print("pre f", a)
f(a)
print("post f", a)

as a is een c int en niet een geheel getal van python en wordt blijkbaar door verwijzing doorgegeven. je moet echter voorzichtig zijn, want er kunnen vreemde dingen gebeuren en wordt daarom niet geadviseerd


Antwoord 29

Hoe zit het met dataklassen? Het stelt je ook in staat om typebeperking toe te passen (ook bekend als “type hint”).

from dataclasses import dataclass
@dataclass
class Holder:
    obj: your_type # Need any type? Use "obj: object" then.
def foo(ref: Holder):
    ref.obj = do_something()

Ik ben het met mensen eens dat je in de meeste gevallen beter kunt overwegen het niet te gebruiken.

En toch, als we het hebben over contextenhet is de moeite waard om het op die manier te weten.

Je kunt wel een expliciete contextklasse ontwerpen. Bij prototyping geef ik de voorkeur aan dataklassen, gewoon omdat het gemakkelijk is om ze heen en weer te serialiseren.

Proost!


Antwoord 30

Waarschijnlijk niet de meest betrouwbare methode, maar dit werkt, houd er rekening mee dat je de ingebouwde str-functie overbelast, wat meestal iets is dat je niet wilt doen:

import builtins
class sstr(str):
    def __str__(self):
        if hasattr(self, 'changed'):
            return self.changed
        return self
    def change(self, value):
        self.changed = value
builtins.str = sstr
def change_the_value(val):
    val.change('After')
val = str('Before')
print (val)
change_the_value(val)
print (val)

Other episodes