2013-05-09 22 views
5

Utilizzo di Python, esiste un modo per memorizzare un riferimento a un riferimento, in modo da poter modificare ciò a cui fa riferimento tale riferimento in un altro contesto? Ad esempio, supponiamo di avere la seguente classe:Memorizzare un riferimento a un riferimento in Python?

class Foo: 
    def __init__(self): 
     self.standalone = 3 
     self.lst = [4, 5, 6] 

Vorrei creare qualcosa di analogo a quanto segue:

class Reassigner: 
    def __init__(self, target): 
     self.target = target 
    def reassign(self, value): 
     # not sure what to do here, but reassigns the reference given by target to value 

Tale che il seguente codice

f = Foo() 
rStandalone = Reassigner(f.standalone) # presumably this syntax might change 
rIndex = Reassigner(f.lst[1]) 
rStandalone.reassign(7) 
rIndex.reassign(9) 

comporterebbe f.standalone uguale a 7 e f.lst uguale a [4, 9, 6].

In sostanza, questo sarebbe un analogo a un puntatore al puntatore.

+0

Penso che il punto di riferimento sia che non si dovrebbe essere in grado di farlo . –

+0

Come intendete usare questo? – FogleBird

+2

Se vuoi il C++, sai dove trovarlo ... – pydsigner

risposta

5

In breve, non è possibile. Affatto. L'equivalente più vicino memorizza un riferimento all'oggetto di cui si desidera riassegnare il membro/elemento, oltre al nome dell'attributo/indice/chiave e quindi utilizza setattr/setitem. Tuttavia, questo produce la sintassi del tutto diversa, e si deve distinguere tra i due:

class AttributeReassigner: 
    def __init__(self, obj, attr): 
     # use your imagination 
    def reassign(self, val): 
     setattr(self.obj, self.attr, val) 

class ItemReassigner: 
    def __init__(self, obj, key): 
     # use your imagination 
    def reassign(self, val): 
     self.obj[self.key] = val 

f = Foo() 
rStandalone = AttributeReassigner(f, 'standalone') 
rIndex = ItemReassigner(f.lst, 1) 
rStandalone.reassign(7) 
rIndex.reassign(9) 

realtà ho usato qualcosa di molto simile, ma i casi d'uso validi sono pochi e lontani tra loro. Per i membri globali/modulo, è possibile utilizzare l'oggetto modulo o globals(), a seconda se si è all'interno del modulo o al di fuori di esso. Non esiste alcun equivalente per le variabili locali - il risultato di locals() non può essere utilizzato per cambiare i locus in modo affidabile, è utile solo per ispezionando.

In realtà ho usato qualcosa di molto simile, ma i casi d'uso validi sono pochi e distanti tra loro.

2

Risposta semplice: non è possibile.
Risposta complicata: è possibile utilizzare lambda. Una specie di.

class Reassigner: 
    def __init__(self, target): 
     self.reassign = target 

f = Foo() 
rIndex = Reassigner(lambda value: f.lst.__setitem__(1, value)) 
rStandalone = Reassigner(lambda value: setattr(f, 'strandalone', value)) 
rF = Reassigner(lambda value: locals().__setitem__('f', value) 
+0

Perché '__setitem__' e non solo l'accesso diretto all'elemento con' [] '? – Eric

+1

Duh, perché quello non dovrebbe essere un lambda – Eric

+2

Come Eric sembra aver realizzato :), 'f.lst [1] = valore' non è un'espressione, ma' f.lst .__ setitem __ (1, valore) 'è . – chepner

0

Questo funzionerebbe per il contenuto degli oggetti contenitore. Se non ti dispiace l'aggiunta di un livello di indirezione alle variabili (che avresti bisogno nel caso C puntatore a puntatore in ogni caso), si potrebbe:

class Container(object): 

    def __init__(self, val): 
     self.val = val 

class Foo(object): 

    def __init__(self, target): 
     self.standalone = Container(3) 
     self.lst = [Container(4), Container(5), Container(6)] 

E non sarebbe davvero bisogno del oggetto riassegnatore.

Class Reassigner(object): 

    def __init__(self, target): 
     self.target = target 
    def reassign(self, value): 
     self.target.val = value 
1

Se è necessario rinviare gli incarichi; è possibile utilizzare functools.partial (o solo lambda):

from functools import partial 

set_standalone = partial(setattr, f, "standalone") 
set_item = partial(f.lst.__setitem__, 1) 
set_standalone(7) 
set_item(9) 

Se reassign è l'unica operazione; non hai bisogno di una nuova classe. Le funzioni sono cittadini di prima classe in Python: è possibile assegnarle a una variabile, archiviare in un elenco, passare come argomenti, ecc.

+0

'operator.setitem' rende il caso articolo simmetrico al caso di attributo, e ha un aspetto migliore. – delnan

+0

@delnan: Ci ho pensato ma volevo dimostrare che 'partial' funziona anche sui metodi (non solo sulle funzioni) e ci sono anche metodi speciali in Python. Altrimenti 'operator.setitem' potrebbe essere preferibile perché rimanda la ricerca' __setitem__' (le eccezioni sono nel sito del chiamante). – jfs

Problemi correlati