2016-06-28 12 views
8

Quindi, stavo guardando il discorso di Raymond Hettinger Transforming Code into Beautiful, Idiomatic Python e lui porta su questo modulo di iter di cui non ero mai a conoscenza. Il suo esempio è la seguente:Quali sono gli usi di iter (callable, sentinel)?

Invece di:

blocks = [] 
while True: 
    block = f.read(32) 
    if block == '': 
     break 
    blocks.append(block) 

Usa:

blocks = [] 
read_block = partial(f.read, 32) 
for block in iter(read_block, ''): 
    blocks.append(block) 

dopo aver verificato la documentation di iter, ho trovato un esempio simile:

with open('mydata.txt') as fp: 
    for line in iter(fp.readline, ''): 
     process_line(line) 

Questo mi sembra abbastanza utile, ma mi stavo chiedendo se tu fossi Pythonis Conoscete tutti gli esempi di questo costrutto che non coinvolgono i loop di lettura I/O? Forse nella libreria standard?

mi viene in mente esempi molto artificiosa, come il seguente:

>>> def f(): 
...  f.count += 1 
...  return f.count 
... 
>>> f.count = 0 
>>> list(iter(f,20)) 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 
>>> 

Ma ovviamente questo non è più utile che il built-in iterabili. Inoltre, mi sembra un odore di codice quando assegni lo stato a una funzione. A quel punto, probabilmente avrei dovuto lavorare con una classe, ma se ho intenzione di scrivere una classe, potrei anche implementare il protocollo iteratore per qualunque cosa io voglia realizzare.

+2

per le persone che downvoted la questione o votato per chiudere, apprezzerei un feedback su come fare meglio questa domanda. –

risposta

4

come regola generale, i principali usi che ho visto per due arg iter implicano la conversione funzioni simili alle API C (stato implicito, no concetto di iterazione) agli iteratori. Gli oggetti simili a file sono un esempio comune, ma si presenta in altre librerie che contengono in modo errato le API C. Lo schema che ti aspetteresti sarebbe quello visto in API come FindFirstFile/FindNextFile, in cui una risorsa viene aperta e ogni chiamata avanza nello stato interno e restituisce un nuovo valore o una variabile di indicatore (come NULL in C). Il wrapping in una classe che implementa il protocollo iterator è solitamente il migliore, ma se devi farlo da solo, mentre l'API è un built-in di livello C, il wrapping può finire per rallentare l'uso, dove due arg iter, implementati in C come bene, può evitare la spesa di esecuzione di codice byte aggiuntivo.

Altri esempi comprendono oggetti mutabili che vengono modificati durante il ciclo stesso, ad esempio, annodare in ordine inverso su linee in un ByteArray, rimuovendo la riga sola volta completata l'elaborazione:

>>> from functools import partial 
>>> ba = bytearray(b'aaaa\n'*5) 
>>> for i in iter(partial(ba.rfind, b'\n'), -1): 
...  print(i) 
...  ba[i:] = b'' 
... 
24 
19 
14 
9 
4 

Un altro caso è quando utilizzando affettando in modo progressivo, ad esempio, un modo efficace (se ammisamente brutto) per raggruppare un iterabile in gruppi di articoli n consentendo al gruppo finale di essere inferiore a n elementi se l'input iterabile non è un multiplo pari di n elementi di lunghezza (questo in realtà l'ho usato, anche se di solito uso itertools.takewhile(bool invece di due arg iter):

# from future_builtins import map # Python 2 only 
from itertools import starmap, islice, repeat 

def grouper(n, iterable): 
    '''Returns a generator yielding n sized tuples from iterable 

    For iterables not evenly divisible by n, the final group will be undersized. 
    ''' 
    # Keep islicing n items and converting to groups until we hit an empty slice 
    return iter(map(tuple, starmap(islice, repeat((iter(iterable), n)))).__next__,()) # Use .next instead of .__next__ on Py2 

Un altro uso: Scrittura di più oggetti in salamoia per un singolo file, seguito da un valore sentinella (None per esempio), in modo che quando deserializzazione, si può usare questo linguaggio, invece di dover ricordare in qualche modo il numero di articoli in salamoia, o che hanno bisogno di chiamare load più e più volte fino a quando EOFError:

with open('picklefile', 'rb') as f: 
    for obj in iter(pickle.Unpickler(f).load, None): 
     ... process an object ... 
+0

Grazie per lo sfondo delle funzioni di conversione simili alle API C, è esattamente quello che stavo cercando. –

8

Ecco un esempio stupido mi si avvicinò con:

from functools import partial 
from random import randint 

pull_trigger = partial(randint, 1, 6) 

print('Starting a game of Russian Roulette...') 
print('--------------------------------------') 

for i in iter(pull_trigger, 6): 
    print('I am still alive, selected', i) 

print('Oops, game over, I am dead! :(') 

Esempio di output:

$ python3 roulette.py 
Starting a game of Russian Roulette... 
-------------------------------------- 
I am still alive, selected 2 
I am still alive, selected 4 
I am still alive, selected 2 
I am still alive, selected 5 
Oops, game over, I am dead! :(

L'idea è quella di avere un generatore che produce valori casuali, e si desidera un processo una volta un particolare valore è stato selezionato. Potresti ad es. utilizzare questo modello in ogni esecuzione di una simulazione che tenta di determinare il risultato medio di un processo stocastico.

Naturalmente il processo si sarebbe modellazione sarebbe probabilmente molto più complicato sotto il cofano di un semplice lancio di dadi ...

Un altro esempio mi viene in mente sarebbe stato più volte l'esecuzione di una manovra e riesce, indicato da un messaggio di errore vuoto (diciamo solo supporre qui che qualche funzione 3rd party è stato progettato come che, invece, ad esempio, utilizzando eccezioni):

from foo_lib import guess_password 

for msg in iter(guess_password, ''): 
    print('Incorrect attempt, details:', msg) 

# protection cracked, continue... 
+0

Avrei dovuto pensare a funzioni che restituiscono valori casuali! –

Problemi correlati