2016-07-06 30 views
8

Come hai già capito sono un principiante e sto cercando di capire quale sia il "modo Pythonic" di scrivere questa funzione è costruito su So che altri thread potrebbero includere una risposta parziale a questo, ma non so cosa cercare dato che non capisco cosa sta succedendo qui.Capire questa riga: list_of_tuples = [(x, y) per x, y, label in data_one]

questa linea è un codice che il mio amico mi ha mandato, per migliorare il mio codice che è:

import numpy as np 

#load_data: 
def load_data(): 
    data_one = np.load ('/Users/usr/... file_name.npy') 
    list_of_tuples = [] 
    for x, y, label in data_one: 
     list_of_tuples.append((x,y)) 
    return list_of_tuples 

print load_data() 

Il "migliorato" versione:

import numpy as np 

#load_data: 
def load_data(): 
    data_one = np.load ('/Users/usr.... file_name.npy') 
    list_of_tuples = [(x,y) for x, y, label in data_one] 
    return list_of_tuples 

print load_data() 

Mi chiedo:

  1. Cosa sta succedendo qui?
  2. È un modo migliore o peggiore? dal momento che è "Pythonic" presumo che non sarebbe funzionare con altre lingue e quindi forse è meglio abituarsi al modo più generale?
+1

è una lista di comprensione. è più pulito e più pitonico. controllalo su google. inoltre, sarà anche più veloce. è sicuramente un modo migliore. – acushner

+1

è il "cosa sta succedendo qui" non è chiaro dalla versione non migliorata? – mgilson

+0

https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions –

risposta

15
list_of_tuples = [(x,y) for x, y, label in data_one] 

(x, y) è un tuple < - Tutorial collegato.

Questa è una lista comprehension

[(x,y) for x, y, label in data_one] 
# ^        ^
# |  ^comprehension syntax^ | 
# begin list      end list 

data_one è un iterable ed è necessaria per una lista di comprensione. Sotto le coperte sono loop e devono scorrere su qualcosa.

x, y, label in data_one mi dice che posso "decomprimere" questi tre elementi da ogni elemento consegnato dal data_one iterabile. Questo è proprio come una variabile locale di un ciclo for, cambia ad ogni iterazione.

In totale, questo dice:

Fate una lista di tuple che sembrano (x, y) dove ho x, y, and label da ogni elemento consegnato dal iterabile data_one. Metti ogni x e in una tupla all'interno di un elenco chiamato list_of_tuples. Sì, lo so che ho "spacchettato" label e non l'ho mai usato, non mi interessa.

+4

Mi piace la tua spiegazione, solo una cosa che vorrei aggiungere: python supporta '_' come" So che hai altre cose da disfare che non mi interessa, quindi buttale via: '[(x, y) per x, y, _ in data_one] 'è valido, anche se discutibilmente meno leggibile – TemporalWolf

+6

@TemporalWolf Python non ha una sintassi" non interessa ",' _' è solo un nome di variabile regolare –

+4

@StefanoSanfilippo Mentre tu sono corretti in quanto non è una sintassi speciale, vale la pena notare che molti editori riconoscono la variabile '_' come un segnaposto" non importa "e sopprimono gli avvisi di" variabile non utilizzata "ad esso correlati. – ApproachingDarknessFish

3

Questo è chiamato un elenco di comprensione. È simile a un ciclo e può spesso svolgere lo stesso compito, ma genererà un elenco con i risultati. Il formato generale è [operation for variable in iterable]. Ad esempio,

[x**2 for x in range(4)] risulterebbe in [0, 1, 4, 9].

Possono anche essere resi più complicati (come il tuo sopra è) utilizzando più funzioni, variabili e iterabili in una sola comprensione di lista. Ad esempio,

[(x,y) for x in range(5) for y in range(10)].

È possibile trovare ulteriori informazioni su questo here.

+0

buon esempio +1 da parte mia. – piRSquared

+0

Grazie inoltre 1 anche a voi piRSquared –

8

Entrambi i modi sono corretti e funzionano. Probabilmente potresti mettere in relazione la prima cosa con il modo in cui le cose sono fatte in C e in altre lingue. In pratica, esegui fondamentalmente un ciclo for per esaminare tutti i valori e quindi aggiungerlo alla tua lista di tuple.

Il secondo modo è più pitonico ma fa lo stesso. Se dai un'occhiata a [(x,y) for x, y, label in data_one] (questa è una lista di comprensione) vedrai che stai anche facendo un ciclo for sugli stessi dati ma il tuo risultato sarà (x, y) e tutti questi risultati formeranno un elenco. Quindi raggiunge la stessa cosa.

Il terzo modo (aggiunto come risposta dei commenti) utilizza un metodo di sezione.

ho preparato un piccolo esempio simile al vostro:

data = [(1, 2, 3), (2, 3, 4), (4, 5, 6)] 

def load_data(): 
    list_of_tuples = [] 
    for x, y, label in data: 
     list_of_tuples.append((x,y)) 
    return list_of_tuples 

def load_data_2(): 
    return [(x,y) for x, y, label in data] 

def load_data_3(): 
    return [t[:2] for t in data] 

fanno tutti la stessa cosa e ritorno [(1, 2), (2, 3), (4, 5)] ma la loro esecuzione è diverso. Questo è il motivo per cui una comprensione delle liste è un modo migliore per farlo.

Quando faccio funzionare il primo metodo load_data() ottengo:

%%timeit 
load_data() 
1000000 loops, best of 3: 1.36 µs per loop 

Quando si esegue il secondo metodo load_data_2() ottengo:

%%timeit 
load_data_2() 
1000000 loops, best of 3: 969 ns per loop 

Quando si esegue il terzo metodo load_data_3() ottengo:

%%timeit 
load_data_3() 
1000000 loops, best of 3: 981 ns per loop 

il secondo modo, la comprensione delle liste, è più veloce!

+0

Dimostrazione eccellente .. Grazie! Il tempismo –

+0

è un buon punto +1 da parte mia. – piRSquared

+0

Puoi chiarire l'uso di '%% timeit'? – TemporalWolf

4

L'azione è essenzialmente la stessa. Nei nuovi interpreti Python l'ambito delle variabili nella comprensione degli elenchi è più ristretto (non è possibile visualizzare x al di fuori della comprensione).

list_of_tuples = [] 
for x, y, label in data_one: 
    list_of_tuples.append((x,y)) 

list_of_tuples = [(x,y) for x, y, label in data_one] 

Questo tipo di azione si verifica abbastanza spesso che gli sviluppatori Python hanno ritenuto utile utilizzare una sintassi speciale. C'è una funzione map(fn, iterable) che fa qualcosa di simile, ma penso che la comprensione delle liste sia più chiara.

Gli sviluppatori Python amano questa sintassi quanto basta per estenderlo a generatori, dizionari e insiemi. E consentono l'annidamento e le clausole condizionali.

Entrambe le forme utilizzano la tupla decompressione x,y,label in data_one.

Cosa fanno entrambe queste clip? data_one apparentemente è una lista di tuple (o sottoliste) con 3 elementi. Questo codice sta creando una nuova lista con 2 tuple di elementi - 2 dei 3 elementi. Penso che sia più facile vederlo nella comprensione della lista.

È consigliabile avere familiarità con entrambi. A volte l'azione è troppo complicata da inserire nella forma di comprensione.

Un'altra caratteristica della comprensione - non consente effetti collaterali (o almeno è più complicato incorporarli). In alcuni casi potrebbe essere un difetto, ma in generale rende il codice più chiaro.

+0

grande intuizione delle motivazioni. +1 da parte mia. – piRSquared

5

La versione "migliorata" utilizza uno list comprehension. Questo rende il codice declarative (che descrive ciò che si desidera) anziché imperative (che descrive come ottenere ciò che si desidera).

I vantaggi della programmazione dichiarativa sono che i dettagli di implementazione sono per lo più tralasciati e le classi e le strutture dati sottostanti possono eseguire le operazioni in modo ottimale. Ad esempio, una ottimizzazione che l'interprete python potrebbe fare nel tuo esempio sopra, sarebbe quella di pre-allocare la dimensione corretta dell'array list_of_tuples piuttosto che dover ridimensionare continuamente l'array durante l'operazione append().

Per iniziare con la comprensione delle liste, spiegherò il modo in cui normalmente inizio a scriverle. Per un elenco L scrivere qualcosa di simile:

output = [x for x in L] 

per ciascun elemento in L, una variabile viene estratto (il centro x) e può essere utilizzato per formare la lista di output (il x a sinistra). L'espressione sopra non fa assolutamente nulla e output uguale a L. Imperativamente, è simile a:

output = [] 
for x in L: 
    output.append(x) 

Da qui, però, si potrebbe rendersi conto che ogni x è in realtà una tupla che potrebbe essere spacchettati usando tuple assignment:

output = [x for x, y, label in L] 

questo creerà una nuova lista, contenente solo l'elemento x di ciascuna tupla nell'elenco.

Se si voleva mettere in valigia una tupla diverso nella lista di output, basta imballare sul lato sinistro:

output = [(x,y) for x, y, label in L] 

Questo è fondamentalmente ciò che si finisce con in versione ottimizzata.

Si possono fare altre cose utili con list comprehension, come ad esempio i valori solo inserimento conformi a una specifica condizione:

output = [(x,y) for x, y, label in L if x > 10] 

Ecco un tutorial utili su list comprehension che potreste trovare interessante: http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/

Problemi correlati