2014-12-18 9 views
6

Ho riscontrato alcuni problemi durante l'utilizzo della funzione range() per creare list s.
Facendo qualche sperimentazione, ottengo il seguente: Perché range() non restituisce un elenco?

>>> isinstance([], list) 
True 
>>> isinstance(range(10), list) 
False 

Inoltre, leggendo la sua documentazione:

>>> print(range.__doc__) 
range(stop) -> range object 
range(start, stop[, step]) -> range object 

Return a virtual sequence of numbers from start to stop by step. 

Attualmente hanno una soluzione utilizzando list(range()), ma la questione rimane ancora. Cos'è una sequenza virtuale di numeri?

+6

'range()' in python 3 restituisce un iteratore. 'range()' in python 2 restituisce una 'lista'. – tyteen4a03

+2

@ tyteen4a03 - Non esattamente. 'range' restituisce una sequenza pigra di tipo' range'. I suoi articoli sono calcolati su richiesta. Ma puoi riutilizzare gli oggetti 'range' mentre gli iteratori sono una tantum (una volta iterato su di essi, sono esauriti). – iCodez

+2

@iCodez TIL. :) – tyteen4a03

risposta

17

A range() l'oggetto calcola i numeri su richiesta, ad es. quando ripetuti su o quando si tenta di accedere a indici specifici:

>>> r = range(2, 80, 3) 
>>> len(r) 
26 
>>> r[15] 
47 
>>> 42 in r 
False 
>>> r[:10] 
range(2, 32, 3) 

Si tratta di una sequenza in quanto l'oggetto supporta il controllo di appartenenza, l'indicizzazione, affettare e ha una lunghezza, proprio come una lista o una tupla. Ma, a differenza di una lista o di una tupla, in realtà non contiene tutti gli interi nella sequenza in memoria, rendendolo virtuale.

Quando si chiama list() su un oggetto range(), si sta creando una nuova sequenza che contiene tutti i numeri interi che si trovano nella gamma, ma ora si sta memorizzando tutti quei numeri interi:

>>> r_list = list(r) 
>>> r_list 
[2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77] 

Questa è una sequenza di troppo, ma ci vuole più memoria dato che tutti gli interi ora vengono prodotti in anticipo, se li userete o meno. Quindi una lista o una tupla è una sequenza di concreta.

Utilizzando la sys.getsizeof() function, possiamo calcolare la quantità di memoria ogni oggetto usa:

>>> import sys 
>>> sys.getsizeof(r) 
48 
>>> sys.getsizeof(r_list) + sum(sys.getsizeof(i) for i in r_list) 
1072 

L'oggetto elenco utilizza 22 volte la memoria; questo perché contiene 26 oggetti interi.

E per rispondere al commento sulla tua domanda, gli oggetti range() sono noniterators. Gli iteratori producono valori uno alla volta su richiesta, ma non possono essere indicizzati, producono tutti i valori solo una volta e non possono essere indicizzati. È possibile produrre un iteratore da un oggetto range() con la iter() function:

>>> iter(r) 
<range_iterator object at 0x10aea23f0> 
>>> r_iter = iter(r) 
>>> len(r_iter) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: object of type 'range_iterator' has no len() 
>>> list(r_iter) 
[2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35, 38, 41, 44, 47, 50, 53, 56, 59, 62, 65, 68, 71, 74, 77] 
>>> list(r_iter) 
[] 

ma una volta esaurita, l'iteratore non produrrà più la stessa gamma.

Tutto quanto sopra si applica principalmente a Python 3; in Python 2 il tipo è chiamato xrange(), dove è più limitato nelle sue capacità (non supporta l'affettatura e può gestire solo interi < sys.maxint).

+0

Dovresti chiarire che questo si applica solo a Python 3. Il 'range' di Python 2 sembra creare un elenco reale fino a I ' sono stato in grado di determinare. –

+0

@MarkRansom in Python 'xrange()' è lo stesso tipo (anche se un po 'meno versatile). –