2011-12-11 16 views
7

C'è questo codice:Assegnazione di oggetti e tipi fondamentali

# assignment behaviour for integer 
a = b = 0 
print a, b # prints 0 0 
a = 4 
print a, b # prints 4 0 - different! 

# assignment behaviour for class object 
class Klasa: 
    def __init__(self, num): 
     self.num = num 

a = Klasa(2) 
b = a 
print a.num, b.num # prints 2 2 
a.num = 3 
print a.num, b.num # prints 3 3 - the same! 

Domande:

  1. Perché operatore di assegnazione funziona in modo diverso per il tipo fondamentale e oggetto classe (per i tipi fondamentali esso copia per valore, per oggetto classe copia per riferimento)?
  2. Come copiare gli oggetti di classe solo in base al valore?
  3. Come creare riferimenti per tipi fondamentali come in C++ int & b = a?
+2

Fuori linguaggio Python, i termini per riferimento/per valore sono già confusi e confusi. All'interno di Python, il cui modello di dati e il modello di esecuzione sono così speciali, questi termini sono ancora più confusi e confusi e dovrebbero essere evitati. Questa è la mia opinione, ma nota che non sono un esperto di Python. Vedi (http://stackoverflow.com/a/986145/551449) e molti altri post e blog su questo argomento. Sembra che sia necessario studiare un po 'di più i dati e i modelli di esecuzione di Python – eyquem

risposta

11

Questa è una pietra d'inciampo per molti utenti di Python. La semantica di riferimento agli oggetti è diversa da quella a cui i programmatori C sono abituati.

Prendiamo il primo caso. Quando si dice a = b = 0, viene creato un nuovo oggettocon il valore 0 e vengono creati due riferimenti (uno è a e un altro è b). Queste due variabili puntano allo stesso oggetto (il numero intero che abbiamo creato). Ora, eseguiamo a = 4. Viene creato un nuovo oggetto 4 del valore int e viene fatto in modo che a indichi quello. Ciò significa che il numero di riferimenti a 4 è uno e il numero di riferimenti a 0 è stato ridotto di uno.

Confronta questo con a = 4 in C in cui è scritta l'area di memoria a cui "a" indica ". a = b = 4 in C significa che 4 viene scritto su due parti di memoria, una per a e un'altra per .

Ora il secondo caso, a = Klass(2) crea un oggetto di tipo Klass, incrementa il suo conteggio di riferimento di uno e ne aumenta il punto a. b = a prende semplicemente i punti a, fa il punto b alla stessa cosa e incrementa il conteggio dei riferimenti della cosa di uno. È lo stesso di quello che succederebbe se facessi a = b = Klass(2). Provare a stampare a.num e b.num sono gli stessi dal momento che stai dereferenziando lo stesso oggetto e stampando un valore di attributo. È possibile utilizzare la funzione integrata id per verificare che l'oggetto sia lo stesso (id(a) e id(b) restituirà lo stesso identificatore). Ora, si modifica l'oggetto assegnando un valore a uno dei suoi attributi. Dal a allo b punto per lo stesso oggetto, ci si aspetta che la modifica del valore sia visibile quando si accede all'oggetto tramite a o b. E questo è esattamente come è.

Ora, per le risposte alle vostre domande.

  1. L'operatore di assegnazione non funziona in modo diverso per questi due. Tutto ciò che fa è aggiungere un riferimento al valore RValue e fa in modo che il valore di LValue sia quello. È sempre "per riferimento" (sebbene questo termine abbia più senso nel contesto del passaggio dei parametri rispetto alle assegnazioni semplici).
  2. Se si desidera copie di oggetti, utilizzare copy module.
  3. Come ho detto al punto 1, quando si esegue un incarico, si spostano sempre i riferimenti. La copia non viene mai eseguita a meno che non la richieda.
+0

Penso che nell'espressione "passa per riferimento", la parola "riferimento" designa un numero == un indirizzo di memoria. Ma nella maggior parte del tuo testo, prendi la parola "riferimento" con il significato di "un pezzo di memoria che contiene un numero che è l'indirizzo di una posizione di memoria", vale a dire "riferimento" quindi utilizzato come sinonimo di "puntatore". Come "puntatore" e "riferimento" sono parole che hanno significato fluttuante secondo il linguaggio considerato, e Python ha dati speciali e modelli di esecuzione, c'è un odore di ambiguità confusa nel testo, come nella maggior parte dei testi su questo argomento – eyquem

+0

Quando scrivi '' a'', rappresenti l'oggetto (una struttura di bit nella memoria), il riferimento (pezzo di memoria che agisce come una scatola) all'oggetto o l'identificatore? – eyquem

+0

eyquem. Sono d'accordo. Probabilmente dovrei definire i termini prima di parlare di questo dato che è un argomento potenzialmente confuso. –

1

Non funziona in modo diverso. Nel tuo primo esempio, hai modificato a in modo che a e b faccia riferimento a oggetti diversi. Nel tuo secondo esempio, non l'hai fatto, quindi a e b fai ancora riferimento allo stesso oggetto.

Integers, a proposito, sono immutabili. Non puoi modificare il loro valore. Tutto quello che puoi fare è creare un nuovo numero intero e riassociare il tuo riferimento. (Come avete fatto nel vostro primo esempio)

5

Citando Data Model

oggetti sono l'astrazione di Python per i dati. Tutti i dati in un programma Python sono rappresentati da oggetti o da relazioni tra oggetti. (In un senso, e in conformità al modello di Von Neumann di un “computer memorizzato programma” codice è anche rappresentato da oggetti.)

Dal punto di vista di pitone, Fundamental data type è fondamentalmente diverso da C/C++ . Viene utilizzato per mappare i tipi di dati C/C++ in Python. E quindi lasciamo la discussione per il momento e consideriamo il fatto che tutti i dati sono oggetto e sono la manifestazione di qualche classe. Ogni oggetto ha un ID (un po 'come l'indirizzo), un Valore e un Tipo.

Tutti gli oggetti vengono copiati per riferimento. Per ex

>>> x=20 
>>> y=x 
>>> id(x)==id(y) 
True 
>>> 

L'unico modo per avere una nuova istanza è crearne uno.

>>> x=3 
>>> id(x)==id(y) 
False 
>>> x==y 
False 

Questo può sembrare complicato in primo grado, ma per semplificare un po ', pitone fatto alcuni tipi immutabile. Ad esempio non è possibile modificare un string. Devi tagliarlo e creare un nuovo oggetto stringa.

Spesso la copia per riferimento fornisce risultati imprevisti per es.

x=[[0]*8]*8 potrebbe darti la sensazione di creare un elenco bidimensionale di 0 s. Ma in realtà crea una lista del riferimento dello stesso oggetto lista [0] s. Così facendo x [1] [1] finirebbe per cambiare tutte le istanze duplicate allo stesso tempo.

Il modulo Copy fornisce un metodo chiamato deepcopy per creare una nuova istanza dell'oggetto anziché un'istanza superficiale. Ciò è utile quando si intende avere due oggetti distinti e manipolarli separatamente come si intendeva nel secondo esempio.

di estendere il vostro esempio

>>> class Klasa: 
    def __init__(self, num): 
     self.num = num 


>>> a = Klasa(2) 
>>> b = copy.deepcopy(a) 
>>> print a.num, b.num # prints 2 2 
2 2 
>>> a.num = 3 
>>> print a.num, b.num # prints 3 3 - different! 
3 2 
+0

+1 per i riferimenti alla documentazione. –

+0

@Abhijit Quando scrivi _ "copia per riferimento" _ per l'esempio '' x = 20'' quindi '' y = x'', cosa viene copiato? Personnamente, penso che non ci sia rigorosamente nulla che sia stato copiato ed è per questo che l'uso di "copia da" non ha senso in Python in alcune occasioni, forse tutte. – eyquem

+0

I dati interni (un puntatore, in particolare un PyObject *, nell'implementazione C) che viene utilizzato per far sì che le variabili si riferiscano ai valori, vengono copiati. :) –

1

Supponiamo che tu ed io abbiamo un amico comune. Se decido che non mi piace più, è ancora tua amica. D'altra parte, se le faccio un regalo, il tuo amico ha ricevuto un regalo.

L'assegnazione non copia nulla in Python e "copia per riferimento" è da qualche parte tra scomodo e privo di significato (come in effetti fai notare in uno dei tuoi commenti). L'assegnazione fa sì che una variabile inizi a riferirsi a un valore. Non ci sono "tipi fondamentali" separati in Python; mentre alcuni di essi sono integrati, int è ancora una classe.

In in entrambi i casi, l'assegnazione fa sì che la variabile faccia riferimento a qualunque sia la valutazione del lato destro. Il comportamento che stai vedendo è esattamente ciò che dovresti aspettarti in quell'ambiente, secondo la metafora. Se il tuo "amico" è uno int o Klasa, l'assegnazione a un attributo è fondamentalmente diversa dalla riassegnazione della variabile a un'istanza completamente diversa, con il comportamento corrispondentemente diverso.

L'unica vera differenza è che allo int non sono assegnati attributi a cui è possibile assegnare. (Questa è la parte in cui l'implementazione deve effettivamente fare un po 'di magia per limitarti.)

Stai confondendo due concetti diversi di un "riferimento". Il C++ T& è una cosa magica che, se assegnata a, aggiorna l'oggetto di riferimento sul posto e non il riferimento stesso; che non può mai essere "risistemato" una volta inizializzato il riferimento. Questo è utile in una lingua in cui la maggior parte delle cose sono valori. In Python, tutto è un riferimento all'inizio. Il riferimento Python è più simile a un puntatore sempre valido, mai nullo, non utilizzabile per l'aritmetica, automaticamente privo di riferimenti. L'assegnazione fa iniziare il riferimento facendo riferimento a una cosa completamente diversa. Non è possibile "aggiornare l'oggetto di riferimento sul posto" sostituendolo all'ingrosso, perché gli oggetti di Python non funzionano in questo modo. Puoi, naturalmente, aggiornare il suo stato interno giocando con i suoi attributi (se ce ne sono di accessibili), ma quegli attributi sono, loro stessi, anche tutti i riferimenti.

Problemi correlati