2016-04-17 13 views
5

Così ho creato una classe personalizzata in Python che supporta l'iterazione usando il metodo __iter__. Il mio metodo __iter__ è strutturato come un oggetto generatore, utilizzando yield per assegnare ciascuno dei valori nell'istanza della classe.Chiamare iter() su un'istanza della mia classe non crea una "istantanea" dei suoi valori correnti?

Ho constatato che se dico qualcosa come x = iter(my_instance) e quindi modificare i dati memorizzati nel mio esempio, se in seguito a creare un elenco di x, troverò che tale elenco contiene i valori che my_class ha attualmente, piuttosto rispetto ai valori che aveva quando ho chiamato iter().

Quindi la mia domanda è duplice a questo punto. Prima di tutto, è questo che dovrebbe succedere? Se è così, perché funziona così?


Sopra è la cosa principale, ma se vuoi davvero andare oltre, continua a leggere. Ora il motivo per cui sto facendo questa domanda è che ho dovuto creare questa lezione come incarico all'università. Il mio professore ha fornito un programma che eseguirà una serie di test su questa classe che ho creato per darmi una buona idea se l'ho fatto correttamente o meno la prima volta.

Il mio programma passa ogni singolo controllo tranne l'ultimo, che in pratica fa ciò che è descritto sopra. Il problema è che si aspetta che l'elenco creato da una precedente chiamata a iter(my_object) rifletta solo i valori memorizzati in my_object nel momento in cui è stato chiamato iter(). La mia reazione iniziale dopo aver osservato questo aspetto era che il mio professore intendeva ottenere un risultato simile semplicemente chiamando list(my_object), ma io sono lo studente qui, quindi sembra molto più probabile che ho semplicemente codificato il mio metodo __iter__ in alcuni molto strani modo, che sta causando questo comportamento. Oppure Python ha cambiato il modo in cui la funzione iter() funziona di recente?


preferirei non pubblicare il mio codice, dal momento che è per una classe, e che potrebbe tecnicamente essere considerato barare (so che è stupido e inutile, sorry). Tuttavia, posso collegare la specifica, che è giusta here (in particolare parte 1, la classe Bag).

+1

L'iteratore e la classe utilizzano lo stesso oggetto elenco sottostante per l'archiviazione. Questo è il motivo per cui entrambi vedranno un cambiamento quando si modificano i dati. – Nayuki

+0

Se si desidera "congelare" l'iteratore, è possibile convertirlo esplicitamente in un elenco (o in un altro tipo di dati di consumo). Gli iteratori sono solitamente pigri, il che spiega perché il tuo ha il comportamento descritto. –

+1

Quando si itera su oggetti mutabili (e la tua borsa è mutabile), anche gli iteratori della libreria standard mostreranno lo stesso comportamento: opereranno sull'iterazione nello stato corrente, riflettendo le modifiche. – Cyb3rFly3r

risposta

1

L'assegnazione appare per darvi un forte indizio di come si dovrebbe andare su questo

Assicurarsi che l'iteratore produce quei valori nella Borsa al momento l'iteratore inizia l'esecuzione; quindi la modifica della borsa durante l'iterazione non influirà sui valori che produce.

Suggerimento: scrivere questo metodo come una chiamata a un generatore locale, passando una copia del dizionario (trattata nella lezione di venerdì nella quarta settimana).

+0

Oh uomo, ho continuato a guardare la seconda parte di quello, e pensando che significasse "creare un generatore" e perdere il primo bit. Grazie compagno! – LandGod

2

come (credo) @ Cyb3rFly3r stava cercando di dire, il comportamento di iterare su una classe mutabile che fa questo è altamente discordante con il modo in cui funziona per le classi container nella libreria standard - e sarebbe quindi una scelta progettuale piuttosto discutibile da fare solo su quelle basi nel mondo reale.

Corrisponde anche a uno dei motivi principali per cui il concetto di iteratore è stato introdotto in Python, che consisteva nel rendere inutili le copie separate dei contenuti degli oggetti contenitore in modo che potessero essere ripetuti.

Comunque, questo è esattamente ciò che è necessario fare nel metodo __iter__() (e suona come ciò che è nel suggerimento da Particolare 11 della linked specification mostrato in Peter Gibson's answer sta suggerendo, copia dizionario che memorizza tutte le informazioni in un'istanza Bag, sia essa una semplice dict ionica, una defaultdict o qualsiasi altra cosa).

Si potrebbe ottimizzare le cose (minimizzando la memoria aggiuntiva richiesta per la copia e la facilità di internamente iterazione di esso) creando un interno list del suo contenuto, piuttosto che fare una copia di tutta la dei dati probabilmente più complesso struttura. ** ** Suggerimento: Facendo questo sarebbe probabilmente molto simile a quello che si sta andando ad avere bisogno di fare per implementare il metodo __repr__() così i suoi risultati sembrano:

        Bag ([ 'a', 'c', 'b', 'b', 'd', 'd', 'd'])

come mostrato nel dettaglio 3 della specifica.

Dato che hai scelto di non pubblicare alcun codice, è difficile aiutarti ulteriormente.

+0

Ok, questo ha senso ora. Dopo aver letto questo e ciò che ha scritto Perter Gibson, tutto ha un senso. Ho appena immaginato che ci venisse chiesto di usare le istruzioni del generatore, dato che lo stavamo imparando, sono stato preso alla sprovvista dall'idea di fare le cose in modo meno efficiente. – LandGod

+1

Si può ancora finire con l'uso di quelle che chiamate istruzioni generatore nella vostra implementazione - solo loro andranno in iterazione sulla copia o sulla struttura dati alternativa che avete deciso di utilizzare per fornire il comportamento desiderato. – martineau

+0

Sì, è esattamente quello che ho finito per fare. – LandGod

Problemi correlati