2013-12-12 18 views
26

cos'è la valutazione lazy in python?Pigro valutazione pigro

un sito detto:

In Python 3.x la funzione range() restituisce un oggetto particolare intervallo che calcola elementi della lista a richiesta (valutazione pigrizia o differito):

>>> r = range(10) 
>>> print(r) 
range(0, 10) 
>>> print(r[3]) 
3 

quello si intende per questo?

+1

Creare un generatore (es. 'def' che contiene' yield') che causa un effetto collaterale come 'print' prima che produca un valore. Quindi avvolgere il generatore con un ritardo di un secondo ogni iterazione. Quando si verificano le stampe? Il 'range' di Python 3 (molto simile a' xrange' in Python 2) funziona in questo modo: i calcoli non vengono eseguiti fino a quando non vengono richiesti. Questo è ciò che significa "valutazione pigra". – user2864740

risposta

42

L'oggetto restituito da range() (o xrange() in Python2.x) è noto come generator.

Invece di memorizzare l'intera gamma, [0,1,2,..,9], nella memoria, il generatore memorizza una definizione di (i=0; i<10; i+=1) e calcola il valore successivo solo quando necessario (AKA minimale di valutazione).

In sostanza, un generatore consente di tornare una lista come la struttura, ma qui ci sono alcune differenze:

  1. Un elenco memorizza tutti gli elementi al momento della creazione. Un generatore genera l'elemento successivo quando è necessario.
  2. Un elenco può essere ripetuto per quanto è necessario, un generatore può essere iterato solo su esattamente una volta.
  3. Un elenco può ottenere elementi per indice, un generatore non può - genera solo valori una volta, dall'inizio alla fine.

Un generatore può essere creata in due modi:

(1) Molto simile alla lista di comprensione:

# this is a list, create all 5000000 x/2 values immediately, uses [] 
lis = [x/2 for x in range(5000000)] 

# this is a generator, creates each x/2 value only when it is needed, uses() 
gen = (x/2 for x in range(5000000)) 

(2) Come funzione, utilizzando yield per restituire il valore successivo:

# this is also a generator, it will run until a yield occurs, and return that result. 
# on the next call it picks up where it left off and continues until a yield occurs... 
def divby2(n): 
    num = 0 
    while num < n: 
     yield num/2 
     num += 1 

# same as (x/2 for x in range(5000000)) 
print divby2(5000000) 

Nota: Anche se range(5000000) è un generatore in Python3.x, [x/2 for x in range(5000000)] è ancora un elenco. range(...) fa il suo lavoro e genera x uno alla volta, ma l'intero elenco dei valori x/2 verrà calcolato quando questo elenco viene creato.

+0

Questa è stata una bella risposta dettagliata, bcorso. :) +1 – zx81

+3

In realtà, 'range' (o' xrange' in 2.x) non * restituisce un generatore. Un generatore è un iteratore - per ogni generatore 'g' puoi chiamare' next (g) '. Un oggetto 'range' è in realtà un iterable. Puoi chiamare 'iter' su di esso per ottenere un iteratore, ma non è un iteratore stesso (non puoi chiamare' next' su di esso). Tra le altre cose, ciò significa che è possibile eseguire iterazioni su un singolo oggetto intervallo più volte. –

7

In breve, la valutazione lazy indica che l'oggetto viene valutato quando è necessario, non quando viene creato.

In Python 2, gamma restituirà un elenco - questo significa che se gli date un gran numero, si calcolerà la gamma e tornare al momento della creazione:

>>> i = range(100) 
>>> type(i) 
<type 'list'> 

In Python 3, tuttavia si ottiene un oggetto gamma speciale:

>>> i = range(100) 
>>> type(i) 
<class 'range'> 

solo quando si consuma, sarà in realtà essere valutato - in altre parole, verrà restituito solo i numeri nell'intervallo se ne hai effettivamente bisogno.

+0

Grazie per aver definito "pigro" :) – BoltzmannBrain

0

Un repository Github denominato python patterns e wikipedia ci dice che cosa è la valutazione lazy.

Ritardi eval di un expr finché non è necessario il suo valore ed evita evals ripetute.

range in python3 non è una valutazione lazy completa, perché non evita la valutazione ripetuta.

Un esempio più classico per la valutazione pigra è cached_property:

import functools 

class cached_property(object): 
    def __init__(self, function): 
     self.function = function 
     functools.update_wrapper(self, function) 

    def __get__(self, obj, type_): 
     if obj is None: 
      return self 
     val = self.function(obj) 
     obj.__dict__[self.function.__name__] = val 
     return val 

Il cached_property (a.k.a lazy_property) è un decoratore che convertono un func in una proprietà valutazione pigra. La prima volta che si accede alla proprietà, la funzione viene chiamata per ottenere il risultato e quindi il valore viene utilizzato la prossima volta che si accede alla proprietà.

es:

class LogHandler: 
    def __init__(self, file_path): 
     self.file_path = file_path 

    @cached_property 
    def load_log_file(self): 
     with open(self.file_path) as f: 
      # the file is to big that I have to cost 2s to read all file 
      return f.read() 

log_handler = LogHandler('./sys.log') 
# only the first time call will cost 2s. 
print(log_handler.load_log_file) 
# return value is cached to the log_handler obj. 
print(log_handler.load_log_file) 

Per usare una parola corretta, un oggetto generatore pitone come gamma sono più come progettato attraverso call_by_need modello, piuttosto che valutazione pigra