2012-06-12 13 views
7

ho presentato una richiesta di pull con questo codice:parentesi quadre non necessari list comprehension quando viene utilizzato in una funzione

my_sum = sum([x for x in range(10)]) 

Uno dei recensori hanno suggerito questo, invece:

my_sum = sum(x for x in range(10)) 

(la differenza è solo che mancano le parentesi quadre).

Sono stato sorpreso che la seconda forma sembra essere identica. Ma quando ho cercato di utilizzarlo in altri contesti in cui la prima opera, non riesce:

y = x for x in range(10) 
     ^SyntaxError !!! 

sono le due forme identiche? C'è qualche motivo importante per cui le parentesi quadre non sono necessarie nella funzione? O è solo qualcosa che devo sapere?

+3

[Questo link] (http://wiki.python.org/moin/Generators) potrebbe essere utile anche. – jadkik94

+0

Ci sono anche le definizioni di dizionario e set, nel caso non ne abbiate sentito parlare. – Amr

risposta

15

Questa è un'espressione generatore. Per farlo funzionare nel caso standalone, utilizzare parentesi:

y = (x for x in range(10)) 

e y diventa un generatore. È possibile eseguire iterazioni sui generatori, quindi funziona dove è previsto un iterable, come la funzione sum.

Esempi di utilizzo e le insidie:

>>> y = (x for x in range(10)) 
>>> y 
<generator object <genexpr> at 0x0000000001E15A20> 
>>> sum(y) 
45 

stare attenti quando mantenendo i generatori in giro, si può solo andare attraverso di loro una volta. Così, dopo il precedente, se si tenta di utilizzare sum di nuovo, questo accadrà:

>>> sum(y) 
0 

Quindi, se si passa un generatore di cui in realtà è previsto un elenco o un set o qualcosa di simile, bisogna stare attenti. Se la funzione o la classe memorizza l'argomento e tenta di ripetere su di esso più volte, si verificheranno dei problemi. Ad esempio si consideri questo:

def foo(numbers): 
    s = sum(numbers) 
    p = reduce(lambda x,y: x*y, numbers, 1) 
    print "The sum is:", s, "and the product:", p 

fallirà se mano un generatore:

>>> foo(x for x in range(1, 10)) 
The sum is: 45 and the product: 1 

Si può facilmente ottenere un elenco dei valori di un generatore produce:

>>> y = (x for x in range(10)) 
>>> list(y) 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

È può utilizzare questo per correggere l'esempio precedente:

>>> foo(list(x for x in range(1, 10))) 
The sum is: 45 and the product: 362880 

Tuttavia tieni presente che se si crea un elenco da un generatore, sarà necessario memorizzare ogni valore. Questo potrebbe usare molta più memoria in situazioni in cui hai molti articoli.

Perché utilizzare un generatore nella situazione?

Il consumo di memoria molto inferiore è la ragione per cui sum(generator expression) è meglio di sum(list): La versione generatore deve solo memorizzare un singolo valore, mentre l'elenco variante deve memorizzare valori N. Pertanto dovresti sempre usare un generatore dove non rischi gli effetti collaterali.

+0

Inoltre, le espressioni generatrici utilizzano spesso meno memoria perché non viene creato un elenco intermedio. –

2

primo è la lista comprehnsion Dove seconda è espressione generatore

(x for x in range(10)) 
    <generator object at 0x01C38580> 
    >>> a = (x for x in range(10)) 
    >>> sum(a) 
    45 
    >>> 

Uso tutore per generatori:

>>> y = (x for x in range(10)) 
>>> y 
<generator object at 0x01C3D2D8> 
>>> 
3

Essi non sono identici.

La prima forma,

[x for x in l] 

è una lista di comprensione. L'altro è un generatore di espressione e scritto così:

(x for x in l) 

restituisce un generatore, non una lista.

Se l'espressione del generatore è l'unico argomento in una chiamata di funzione, è possibile saltare le parentesi.

Vedi PEP 289

0

Leggere questa PEP: 289

Per esempio, il codice seguente sommatoria costruirà un elenco completo delle piazze in memoria, iterare su quei valori, e, quando non è più necessario il riferimento , eliminare l'elenco:

sum([x*x for x in range(10)]) 

memoria è conservata utilizzando un generatore di espressione invece:

sum(x*x for x in range(10)) 

Poiché i volumi di dati crescono più grande, espressioni generatore tendono a rendere meglio perché non fanno memoria cache di scarico e permettono Python riutilizzare oggetti tra iterazioni.

prodotto Usa brace un generatore:

>>> y = (x for x in range(10)) 
>>> y 
<generator object <genexpr> at 0x00AC3AA8> 
Problemi correlati