Funzione che manipola la raccolta e restituisce lo stesso tipo

1

Mi stavo chiedendo questo mentre programmavo in python. Se ho una funzione foo che prende una collezione (lista o tupla, forse impostata ecc.) E manipola in qualche modo i suoi elementi, ad es. filtrare, tagliare, aumentare, ecc. se tale funzione restituisse lo stesso tipo? Ad esempio, se accetta qualsiasi raccolta e filtra le cose, cosa dovrebbe restituire come oggetto di output? È importante?

Ci sono anche altri contenitori, come numpy . Quindi ho un numpy object a . Potrebbero esserci operazioni che non posso fare su a usando metodi da numpy e quindi devo convertirli in una lista. Devo controllare un tipo e restituire lo stesso tipo?

Se sì, esiste una tecnica generale per controllare il tipo e restituire lo stesso?

    
posta Celdor 18.01.2018 - 16:34
fonte

2 risposte

3

Generalmente non è possibile restituire una nuova collezione, perché non si sa cosa si aspetta il costruttore. Cioè type(original)(new_values) può o non può funzionare.

Invece, dovresti usare la raccolta più generale che Python conosce: un iterabile. In particolare, questo ti consente di utilizzare comodamente generatori .

Confronta questa funzione restituendo un elenco ...

def only_positive(items):
  out = []
  for x in items:
    if x > 0:
      out.append(x)
  return x

... con una funzione basata sul generatore:

def only_positive(items):
  for x in items:
    if y > 0:
      yield x

Notate che molti builtin che precedentemente funzionavano su liste dove erano stati modificati in funzione del generatore in Python 3. E.g. per Py2, filter() è documentato per restituire:

If sequence is a tuple or string, return the same type, else return a list.

Ma in Py3, filter() è in realtà un oggetto iteratore.

Numpy ha un approccio diverso perché l'intero punto sta eseguendo operazioni su un intero array. In genere, queste funzioni sono

  • accetta e restituisce un singolo valore scalare in cui viene eseguito il calcolo come per un array a elemento singolo o
  • prendi un narray o un oggetto simile ad un array che può essere forzato ad un narray e restituire un narray.

Il tipo di input non è generalmente conservato.

    
risposta data 18.01.2018 - 16:56
fonte
2

Esistono generalmente due tipi di framework di raccolta: quelli senza codice duplicato ma che perdono il tipo di raccolta e quelli che conservano il tipo di raccolta ma hanno il codice duplicato.

La maggior parte rientra nel primo campo, ad es. in .NET, tutto restituisce IEnumerable , in Ruby, tutto restituisce Array , in Python, tutto restituisce un iterabile, ecc. Smalltalk cade nel secondo campo: tutte le operazioni preservano il tipo, ma a costo di duplicare semplicemente il operazioni per ogni tipo.

Scala ha la prima libreria di collezioni su larga scala che conosco che riesce a preservare il tipo senza duplicazioni. L'idea chiave è il concetto di builder , che sono oggetti che sanno come (in modo efficiente) creare collezioni di un tipo specifico. Quindi, un'operazione come filter ha solo bisogno di cercare il builder per il tipo di raccolta e quindi può usare questo builder per creare in modo efficiente la raccolta dei risultati senza dover conoscere in realtà ogni singola raccolta che potrebbe mai esistere. In Scala, è il sistema di tipi (o più precisamente risoluzione implicita ) che si occupa di questa ricerca in fase di compilazione (in modo che non vi sia sovraccarico di runtime), ma può anche essere fatto in fase di runtime.

Si noti, tuttavia, che le operazioni di raccolta di preservazione dei tipi possono avere conseguenze sorprendenti. Ad esempio, non ti aspetti che map cambi la lunghezza di una raccolta, ma

map(Set(1, 2, 3, 4), lambda el: is_even(el)) == Set(true, false)

Inoltre, in alcuni casi, devi effettivamente restituire un tipo più generale o un altro tipo correlato. Ad esempio, un ByteSet può contenere solo i numeri 1-255, quindi

map(ByteSet(16), lambda el: el.__str__()) == Set("16")
map(ByteSet(16), lambda el: el ** 2) == IntSet(256)

Quindi, se vuoi creare un framework di collezioni di preservazione dei tipi, puoi guardare il framework delle collezioni di Scala e i suoi builder e il CanBuildFrom macchinario implicito di tipo. Ti suggerirei di guardare la proposta di Strawman per la struttura semplificata e ridisegnata in Scala 2.13 invece della versione attuale poiché quest'ultima è considerata eccessivamente ingegnerizzata e eccessivamente complessa.

    
risposta data 18.01.2018 - 20:58
fonte

Leggi altre domande sui tag