2015-02-05 18 views
5

Ho una domanda su come creare una sottolista (spero che questo sia il termine giusto da usare) da una lista data senza copiare.Python: creare una sottolista senza copiare

Sembra che l'affettatura possa creare sottoliste, ma lo fa con la copia. Ecco un esempio.

In [1]: a = [1,2,3] 

In [2]: id(a) 
Out[2]: 4354651128 

In [3]: b = a[0:2] 

In [4]: b 
Out[4]: [1, 2] 

In [5]: id(b) 
Out[5]: 4354621312 

In [6]: id(a[0:2]) 
Out[6]: 4354620880 

Vedere qui l'id di b e [0: 2] sono diversi, sebbene i loro valori siano gli stessi. Per ricontrollare, cambiare il valore in a, il valore in b non cambia.

In [7]: a[1] = 4 

In [8]: a 
Out[8]: [1, 4, 3] 

In [9]: b 
Out[9]: [1, 2] 

Quindi, per tornare alla mia domanda, come posso creare sottoliste ma senza copiare? Voglio dire, quando il valore di [1] è impostato su 4, b sarà [1, 4].

Ho cercato in giro e non ho trovato molto aiuto (forse non sto usando le parole chiave giuste). Grazie!


Modifiche:

Grazie a tutti per i vostri commenti e le risposte! Ecco cosa ho imparato.

  • Non esiste un modo integrato in Python per creare una visualizzazione di un elenco (o per creare un elenco di sottotitoli senza copiare).
  • Il modo più semplice per eseguire questa operazione è utilizzare la matrice numpy.
  • Sebbene gamma NumPy ha limitazioni sul tipo di dati rispetto al listino, lo fa servire il mio scopo (ad attuare Quicksort senza memoria aggiuntiva)

Ecco lo stesso processo con serie NumPy.

In [1]: import numpy as np 

In [2]: a = np.arange(1,4) 

In [3]: a 
Out[3]: array([1, 2, 3]) 

In [4]: b = a[0:2] 

In [5]: b 
Out[5]: array([1, 2]) 

In [6]: id(b) 
Out[6]: 4361253952 

In [7]: id(a[0:2]) 
Out[7]: 4361254032 

In [8]: a[1] = 4 

In [9]: a 
Out[9]: array([1, 4, 3]) 

In [10]: b 
Out[10]: array([1, 4]) 
+1

Il problema con questo tipo di condivisione è la perdita di memoria: supponiamo che rappresenti un elenco di sezioni [a: b] utilizzando un riferimento all'elenco e i valori a e b. Quindi, anche se la sezione è molto piccola, impedisce che la lista venga raccolta dai dati inutili, il che potrebbe essere molto costoso. Ma ovviamente puoi definire una classe personalizzata per le sezioni di lista "simboliche" con la rappresentazione sopra. –

+0

perché vuoi farlo? –

+1

Penso che quello che stai descrivendo sia molto vicino al concetto di viste sugli array 'numpy'. Vedi [questo post SO e risposta] (http://stackoverflow.com/questions/4370745/view-onto-a-numpy-array) per qualche discussione sull'argomento. Si avverta però che gli array 'numpy' sono meno flessibili sui tipi di dati che possono contenere, rispetto ai tipici elenchi Python, quindi potrebbero non adattarsi al caso d'uso a seconda dei dati che si desidera includere. – zehnpaard

risposta

4

s' numpy serie oggetti supporta questa idea di creare interdipendenti sotto-liste, avendo affettare ritorno views piuttosto che copie dei dati.

Alterare l'array originale numpy modificherà le visualizzazioni create dall'array e le modifiche a qualsiasi vista verranno riflesse anche nell'array originale. Soprattutto per i set di dati di grandi dimensioni, le visualizzazioni sono un ottimo modo per tagliare i dati in modi diversi, risparmiando sulla memoria.

>>> import numpy as np 
>>> array1 = np.array([1, 2, 3, 4]) 
>>> view1 = array1[1:] 
>>> view1 
array([2, 3, 4]) 
>>> view1[1] = 5 
>>> view1 
array([2, 5, 4]) 
>>> array1 
array([1, 2, 5, 4]) # Notice that the change to view1 has been reflected in array1 

Per ulteriore riferimento, vedere la numpy documentation on views nonché this SO post.

+0

Ho pensato di reinventare la ruota, bella risposta. – mVChr

1

non è possibile se si divide a per ottenere b.

Tutte le operazioni di sezione restituiscono un nuovo elenco contenente gli elementi richiesti. Ciò significa che il seguente fetta restituisce un nuovo (superficiale) copia della lista [1]

[1] https://docs.python.org/2/tutorial/introduction.html

1

Non c'è modo integrato per effettuare questa operazione. È possibile creare la propria classe di tipo elenco che prende come riferimento un elenco e reimplementa tutti i metodi di accesso elenco per operare su di esso.

1

Non c'è modo di farlo con strutture dati Python integrate. Tuttavia, ho creato una classe che fa ciò di cui hai bisogno. Non garantisco che sia privo di bug, ma dovrebbe iniziare.

from itertools import islice 

class SubLister(object): 
    def __init__(self, base=[], start=0, end=None): 
     self._base = base 
     self._start = start 
     self._end = end 

    def __len__(self): 
     if self._end is None: 
      return len(self._base) - self._start 
     return self._end - self._start 

    def __getitem__(self, index): 
     self._check_end_range(index) 
     return self._base[index + self._start] 

    def __setitem__(self, index, value): 
     self._check_end_range(index, "list assignment index out of range") 
     self._base[index + self._start] = value 

    def __delitem__(self, index): 
     self._check_end_range(index, "list assignment index out of range") 
     del self._base[index + self._start] 

    def __iter__(self): 
     return islice(self._base, self._start, self._end) 

    def __str__(self): 
     return str(self._base[self._start:self._end]) 

    def __repr__(self): 
     return repr(self._base[self._start:self._end]) 

    # ...etc... 

    def get_sublist(self, start=0, end=None): 
     return SubLister(base=self._base, start=start, end=end) 

    def _check_end_range(self, index, msg="list index out of range"): 
     if self._end is not None and index >= self._end - self._start: 
      raise IndexError(msg) 

Esempio:

>>> from sublister import SubLister 
>>> base = SubLister([1, 2, 3, 4, 5]) 
>>> a = base.get_sublist(0, 2) 
>>> b = base.get_sublist(1) 

>>> base 
[1, 2, 3, 4, 5] 
>>> a 
[1, 2] 
>>> b 
[2, 3, 4, 5] 
>>> len(base) 
5 
>>> len(a) 
2 
>>> len(b) 
4 

>>> base[1] = 'ref' 
>>> base 
[1, 'ref', 3, 4, 5] 
>>> a 
[1, 'ref'] 
>>> b 
['ref', 3, 4, 5] 
+0

È una buona implementazione, ma un paio di metodi ancora copiano la lista, il che è indesiderabile (come len e iter). – Dunes

+0

Grazie per le correzioni nella tua modifica @Dunes, risulta che era davvero buggy come ho detto. Volevo solo dare al ragazzo un inizio con cui potesse lavorare. – mVChr

+0

Non direi che le modifiche apportate erano correzioni di bug. Il codice era funzionalmente corretto. Era più giusto migliorare l'efficienza della classe che aveva lo scopo di minimizzare la copia della lista. – Dunes

Problemi correlati