Topic: [RISOLTO]Dizionario->Json->Dizionario, mantenere l'ordine delle chiavi  (Letto 282 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline tommyb1992

  • python neanderthalensis
  • ****
  • Post: 289
  • Punti reputazione: 0
    • Mostra profilo
Salvo dei dati in dizionario, poi li trasformo in json e poi li ripesco ritrasformandoli in dizionario, poi li ciclo e li butto dentro una rete neurale, solo che se mi "sposta" l'ordine delle chiavi ovviamente succede un bel casino visto che input X vuole neurone X, input X1 vuole neurone X2 etc...

Come faccio a mentenere l'integrità dell'ordine?

// edit
ho pensato a questa soluzione:

dizionario = OrderedDict(sorted(dizionario.items()))
saveIntoJson(dizionario)
dizionario = readJson()
dizionario = OrderedDict(sorted(dizionario.items()))


Che dite sto in una botte di ferro?
« Ultima modifica: Aprile 29, 2018, 16:25 da tommyb1992 »

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 2.821
  • Punti reputazione: 9
    • Mostra profilo
Re:Dizionario->Json->Dizionario, mantenere l'ordine delle chiavi
« Risposta #1 il: Aprile 28, 2018, 20:52 »
Difficile dirlo. In python 3.7 (o forse già dal 3.6) i dizionari mantengono l'ordine, quindi non c'è più bisogno di avere Ordereddict. Il problema però è json: non credo proprio che la specifica di json imponga di preservare l'ordine delle chiavi, e in effetti così su due piedi mi verrebbe da scommettere che tutti i json-converter in circolazione non garantiscono di preservare l'ordine. E nello specifico non saprei proprio dire se il modulo json preserva l'ordine, dovresti fare esperimenti o cercare in giro, o chiedere sul forum. Ma non ci scommetterei. L'unica possibilità *documentata*, mi sembra è passare sort_keys=True a json.dump(s), che si impegna a ordinare alfabeticamente le chiavi. Questo almeno garantisce la riproducibilità dell'output json, ma ovviamente si impegna a mantere *un certo ordine* (alfabetico), e non un ordine arbitrario.

In generale, se ti serve l'ordine in json, usa le liste mi verrebbe da dire...

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 2.821
  • Punti reputazione: 9
    • Mostra profilo
Re:Dizionario->Json->Dizionario, mantenere l'ordine delle chiavi
« Risposta #2 il: Aprile 29, 2018, 14:19 »
Uhm, a una seconda occhiata, potresti cavartela dopo tutto. Come ricordavo, le specifiche di json *non* obbligano a preservare l'ordine dei dizionari (http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf pag 11).
Tuttavia, se dai un'occhio al sorgente di json, vedi che per encodare un dict cicla semplicemente su dict.items(). E siccome da Python 3.6 in poi dict.items preserva l'ordine di inserimento, direi che non dovresti avere problemi.
(dopo di che, beninteso, resta che stai usando a tuo vantaggio una cosa non-standard per json, e un dettaglio implementativo non documentato di Cpython. Ma insomma, basta esserne consapevole e non dovresti avere problemi).

Offline tommyb1992

  • python neanderthalensis
  • ****
  • Post: 289
  • Punti reputazione: 0
    • Mostra profilo
Re:Dizionario->Json->Dizionario, mantenere l'ordine delle chiavi
« Risposta #3 il: Aprile 29, 2018, 16:25 »
Onestamente non mi sono mai informato sulle specifiche modifiche da versione a versione di python.

Comunque provando, si il dizionario rimaneva "intatto" quando lo printavo (dopo averlo jsonnato e ri-trasformato in dizionario), ma quando lo ciclavo non era per nulla ordinato, ho inserito un sorted e andava in ordine alfabetico e mi va bene, l'importante è che mi piazzi i dati sempre nello stesso punto.

Grazie

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Re:Dizionario->Json->Dizionario, mantenere l'ordine delle chiavi
« Risposta #4 il: Maggio 06, 2018, 01:09 »
A me verrebbe da dire ma perche' tutti insistono ad usare un dizionario quando e' importante l'ordine delle chiavi visto che un dizionario nasce con l'idea che *non* sia importante.
Tanto che stiamo parlando nello stesso post dell'ordine di *inserimento* delle chiavi e dell'ordine alfabetico delle chiavi. Ovviamente se si vuole una struttura dati che mantenga l'ordine alfabetico delle chiavi ci vuole qualche tipo di TreeMap (in Java, C++ e rust e' piuttosto comune), in Python il vuoto cosmico.
Iterare sulle chiavi *in ordine* e' sbagliato (o meglio: e' sbagliato farlo su un dizionario... siccome Python qui fallisce, ci vuole il trucchetto). Volerle salvate in ordine su json e' legittimo.

Ora, detto fra noi: il post iniziale di Raymond e' splendido. Lui ha semplicemente riprogettato l'implementazione per una serie di vantaggi di performance. Il side effect e' che le chiavi sono in ordine di inserimento. Cosa che ovviamente e' lecitissima visto che un dizionario puo' tornarti le chiavi in ogni ordine. A me l'idea di rendere questa cosa "obbligatoria" sembra proprio una cattiva idea. Tipo se dopodomani Raymond si sveglia e trova un'altro modo ancora piu' efficiente che non preserva piu' le chiavi, non si puo' fare. A me sembrerebbe un peccato. La cosa che mi verrebbe da pensare per dare un senso a tutto questo e' che magari i core devs hanno paura che una volta che la gente si accorge che le chiavi sono in ordine di inserimento fara' uso pesante della "feature" (ricordiamo che la maggior parte delle persone non hanno davvero capito a cosa serve un dizionario). A quel punto Python non potrebbe in pratica cambiare implementazione poiche' spaccherebbe il codice a troppa gente. E allora tanto vale renderlo ufficiale.

Poi detto fra noi... il fatto che tu hai questo problema vuole dire che non hai progettato qualcosa in modo opportuno. Tipicamente vuole dire che non dovresti usare e ragionare in termini di dizionari. Non fai ricerche per chiave (che e' lo scopo dei dizionari). Se hai bisogno di tenere due elementi accoppiati... vuoi una lista di coppie. E' anche piu' efficiente.

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 2.821
  • Punti reputazione: 9
    • Mostra profilo
Uhm, diciamo che sono d'accordo all'80%. In generale, se hai bisogno di un dizionario order-preserving, forse dovresti fermarti un attimo a pensarci: è davvero un dizionario, quello che stai cercando?
Detto questo, però... però.

Intanto:

> se si vuole una struttura dati che mantenga l'ordine alfabetico delle chiavi ci vuole qualche tipo di TreeMap
eh ma appunto: una TreeMap non è order-preserving nel senso che ci interessa qui: mantiene le chiavi ordinate, ma *non* nell'ordine di inserimento.

>  in Python il vuoto cosmico
Sì, per il caso d'uso "chiavi ordinate alfabeticamente". D'altro canto, Python ha OrderedDict (e adesso semplicemente dict), che non mi sembra che ci sia in Java out-of-the-box. Pari e patta, direi.

E poi, più interessante:

> Ora, detto fra noi: il post iniziale di Raymond e' splendido.
> Lui ha semplicemente riprogettato l'implementazione per una serie di vantaggi di performance.
> Il side effect e' che le chiavi sono in ordine di inserimento.

Uhm, sì e no. Cioè, sì, Raymond ha progettato un dizionario più veloce. Ma *non* è stato introdotto in Python perchè era più veloce, ma appunto per l'effetto collaterale di essere order-preserving, e questo proprio fin dall'inizio... La storia è interessante e fa capire un po' di problemi.
Il punto qui è che tu puoi dire e ripetere che un dizionario non è order-preserving, e la gente può sicuramente abituarsi all'idea... finché quello che vede è un dizionario. Ma poi ci sono casi dove il dizionario sta dietro le quinte, e tu non lo vedi... e magari ti aspetti delle cose che il dizionario dietro non può darti. Case-in-point, gli argomenti di una funzione: tu ti aspetti che l'ordine degli argomenti sia significativo..., *tranne* che invece per i kwargs l'ordine improvvisamente *non* è significativo (perché sono implementati con un dizionario... naturalmente!).
Ora, ci sono alcuni corner-case dove questo potrebbe darti fastidio, da sempre... ma ovviamente il problema esplode letteralmente e diventa visibile a tutti nel momento in cui tu introduci una cosa come collections.OrderedDict, con l'idea di fare un favore alla gente... salvo che poi di colpo la gente si aspetta che OrderedDict(a=1, b=2) preservi le chiavi nell'ordine in cui ha inserito gli argomenti... solo che gli argomenti sono ancora un dict vecchio stile, non un OrderedDict a loro volta!
Di qui una marea di ticket, e di qui, finalmente, la PEP 468 https://www.python.org/dev/peps/pep-0468. Vale la pena di leggersela, questa pep, perché spiega alcune cose interessanti. Comunque in sostanza la pep introduce l'idea che: "gli argomenti delle funzioni hanno *sempre* un ordine significativo, anche i kwargs".
E come si implementa, dietro le quinte, questa cosa? Ma semplicissimo: introducendo il nuovo dict di Raymond, che oltre a essere più veloce ha anche questo bellissimo side-effect di preservare l'ordine delle chiavi, che adesso ci viene proprio comodo.

E questo succedeva in Python 3.6. Nota che tutto quello che succede "ufficialmente" in questa release è la pep 468, che riguarda l'ordine degli argomenti nelle funzioni. Ma nel frattempo ormai *tutti* i dizionari Python sono diventati order-preserving dietro le quinte, si sparge la voce, eccetera eccetera, e adesso in Python 3.7 la cosa diventa ufficiale. Quindi sì, da un lato è come dici tu: l'order preserving è un side-effect dell'implementazione di Raymond che è "scappato di mano". Ma attenzione: il dict di Raymond, fin dall'inizio, è stato introdotto *proprio* per questo side-effect, non per la velocità (non solo, certo).
E attenzione ancora: il side-effect del nuovo dict è *già* stato utilizzato, appunto per implementare la pep 468. Quindi, anche in assenza di una vera e propria pep specifica sui dizionari order-preserving, di fatto non si può tornare indietro - a meno di rinnegare la 468, o implementarla in un altro modo. Quindi sì, in pratica i dizionari in Python saranno *sempre* order-preserving d'ora in poi, per forza.
Questa è una cattiva idea? E' una buona idea? Boh. Tanto ormai è andata.

Ovviamente non c'è solo il caso di OrderedDict... per esempio, quando devi stampare gli argomenti di una funzione per l'utente, ti dà fastidio che non siano stampati nell'ordine "giusto". Ma poi, in generale, oltre ai dizionari di kwargs, ci sono altri casi in cui un dizionario non-order preserving è una bella scocciatura. Caso tipico, la serializzazione, tutte le volte che hai bisogno di ottenere risultati riproducibili evitando diff spuri. Per questi casi il modulo json da sempre offre l'opzione di ordinare alfabeticamente le chiavi nell'output, ma ci sono casi in cui questo comunque non basta (per esempio quando lavori cross-locale e l'ordine di collazione potrebbe variare).



Questo ci porta nei paraggi del caso dell'OP, che però forse è diverso, non so. Ora, per come la vedo io, se l'OP ha bisogno di serializzare oggetti Python in modo fedele, forse dovrebbe usare pickle. Ma capisco che uno non abbia voglia di usare pickle per mille motivi, quindi vada per json. Se l'OP usa il modulo json per serializzare un dizionario in json, allora dovrebbe essere consapevole di due cose. Primo, dal lato Python l'order preserving dell'output si basa su un dettaglio implementativo non documentato: in pratica, nel codice di json le chiavi vengono iterate con for k, v in dict.items() e siccome adesso i dict sono order-preserving, siamo a cavallo. A rigore, quella riga di codice non corrisponde a un'api documentata e potrebbe cambiare senza preavviso in futuro: in pratica però, per introdurre una regressione proprio in quel punto, ci vorrebbe un atto di deliberata malvagità. Non credo che l'OP rischi molto a fidarsi del fatto che ormai json serializzi in modo order-preserving.
Seconda cosa: l'OP dovrebbe comunque tenere presente che la specifica di json *non* impone che i dizionari siano order-preserving (o che l'ordine delle chiavi sia significativo in qualunque modo). Quindi, di conseguenza, nessun de/serializzatore di json è tenuto a conservare l'ordine delle chiavi. Il modulo json adesso lo fa, ma è una feature non richiesta. Quindi se il suo output json dovesse essere consumato da un altro deserializzatore (anche scritto in Python) diverso dal modulo json, i risultati *non* sono garantiti.
Ora, nella mia modesta esperienza, quando qualcuno produce un output in json, prima o poi a qualcun altro verrà in mente di usarlo, al di là degli scopi e dell'ambito in cui era stato concepito inizialmente. Quindi l'OP dovrebbe restare acutamente consapevole di stare introducento una semantica (l'ordine significativo delle chiavi del suo dizionario) che ha perfettamente senso nell'ambito Python (3.6+) del suo programma, ma che invece non trova corrispondenza nell'ambito del suo output json. Come minimo, dovrebbe documentare molto chiaramente la cosa. Come minimo.
« Ultima modifica: Maggio 07, 2018, 12:49 da RicPol »

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
>  in Python il vuoto cosmico
Sì, per il caso d'uso "chiavi ordinate alfabeticamente". D'altro canto, Python ha OrderedDict (e adesso semplicemente dict), che non mi sembra che ci sia in Java out-of-the-box. Pari e patta, direi.

No, davvero. Senti, siamo oggettivi. Faccio questo mestiere da tempo. HashMap/dizionari? Tutti i giorni. "TreeMap" e simili? Si usano. Ovviamente avendo un briciolo di familiarita' in algoritmi, e' anche ovvio quando. Tra l'altro, l'ordered dict standard e' implementato esattamente come farei se avessi 10 minuti di tempo per farlo. E' un dict cui hanno ficcato dentrro una lista per tenere traccia di quando hai messo le chiavi.

Nient'altro. Se il tuo tipo di chiave si presta a sort invece che a hash (si, esistono...), non ti aiuta (che invece e' uno dei motivi per avere treemap). Oggettivamente (l'unica cosa che fa e' ... beh. La faccenda e' che qualcuno lo ha implementato e che PHP e RUBY (esatto...) avevano questa "feature". Ora fare l'ordereddict e' cheap. Specie perche' ha nel contratto limiti che vengono da come e' implementato.

Ovvero tiene in memoria l'ordine in cui le chiavi vengono inserite *la prima volta* (quindi per esempio non va nemmeno bene boh, per tenere traccia delle scritture). E' semplicemente la cosa piu' semplice che ti da l'illusione di avere ordine. Questo perche' la gente non conosce ASG e si sorprende sull'iterazione. Che non dovrebbe manco essere quello che fai con i dizionari: cioe' comodo e'... ma se davvero quello che devi fare e' iterare, sei piu' efficiente con una lista di tuple. Gia'. Quindi entra OrderedDict.

E tu vuoi paragonare questo con l'immensa libreria di strutture dati che e' in Java collections? No, mi dispiace. L'offerta di Python nell'area e' estremamente limitata. Poi che a chi non sa cosa si perde vada bene e' un conto. Anche che con un po' di magheggi ce ne si esce e' vero (e poi saltano fuori quarantasette implementazioni parzialmente buggate di priority queue costruite su heapq).

E io capisco che a te sia simpatico ordered dict. Ma e' davvero una pezza. La meta' delle volte la gente lo usa creando da 0 un dizionario ogni volta inserendo le chiavi nell'ordine che vogliono, sicche' poi "siano ordinate". Cioe' stanno facendo hacking del fatto che l'ordine di inseriemento e' l'ordine di uscita (invece che l'ordinamento naturale o richiesto delle chiavi) e tanti saluti. E in generale in questi casi non vogliono *davvero* un dizionario: vogliono una sequenza di chiave valore. E no, quello non e' un dizionario. Sebbene un dizionario ti dia l'illusione di esserlo. E qui bisognerebbe parlare di gerarchie astratte e di un livello di complicazione che potrebbe davvero essere eccessivo.

Ci si vive? Certo. E' ideale? No. E' la pezza ad una carenza. Oggettivamente inadeguata. Meno e' presente in roba come PHP (e forse nemmeno), C (che si fa come vanto di essere minimale nella stdlib -- e cinquantamila implementazioni di linked list piu' tardi forse si sono accorti che non era una buona idea). E Golang. Cioe', Golang e' confrontabile con Python ( e considerato largamente inadeguato nell'area ).

Ogni volta che mi trovo a scrivere codice algoritmico in Python, mi sento come un carpentiere che ha solo martello e chiodi. E poi figuriamoci... ci so costruire tutto. Solo che e' appunto, una pezza su una pezza. Ah, certo... posso prendere dipendenze. E bella questa su ActiveState (ma voglio veramente prendere una ricetta da li e metterla in produzione?) oppure ci sono 27 librerie (di cui 20 scritte da studentelli alle prime armi, 5 dai loro docenti -- che non hanno mai messo in produzione nulla -- e una e' quella buona). Ora, si scheza... ma davvero, se sai quello che fai, sai anche come uscirne... ma sai anche che se ce l'avessi preconfezionato faresti prima.

> Uhm, sì e no. Cioè, sì, Raymond ha progettato un dizionario più veloce. Ma *non* è stato introdotto in Python perchè era più veloce, ma appunto per l'effetto collaterale di essere order-preserving, e questo proprio fin dall'inizio... La storia è interessante e fa capire un po' di problemi.

E questo e' il dramma. D'altra parte ormai Python non lo considero nemmeno piu' per le robe semplici. Dal mio punto di vista, e' debito tecnico allo stato puro. E' qualcosa che so che dovro' buttare e riscrivere. MA a tutti i livelli... faccio spesso tuning per avere performance. Bene, in Python e' per non fare finire tutto a rotoli. In Java e' perche' posso fare di piu' con meno. E Java e' un linguaggio *terribile*. E la JVM non mi piace. Ma alla fine, con un po' di buon senso se ne ha ragione.

Ora che ci penso, tutti i pezzi di codice che ho visto in giro che andavano a rotoli per memory leak, performance issues, vacca boia e compagnia hanno un comun denominatore. Python. (Non che Ruby o PHP siano diversi in questo... Perl lo e': di solito li il problema e' mantenere il codice, ma una volta scritto e limato un po' le performance non sono un problema... i bachetti si -- anche perche' credo che si incontra un limite di complessita' di gestione prima che in Python, e quindi e' in media software piu' semplice).

> Il punto qui è che tu puoi dire e ripetere che un dizionario non è order-preserving, e la gente può sicuramente abituarsi all'idea... finché quello che vede è un dizionario.

Non e' che la gente "si abitua" all'idea. Non piu' di quanto la gente si "abitui all'idea di avere un intestino e di usarlo per cacare". E' semplicemente cosi' che funziona.

Citazione
Ma poi ci sono casi dove il dizionario sta dietro le quinte, e tu non lo vedi... e magari ti aspetti delle cose che il dizionario dietro non può darti. Case-in-point, gli argomenti di una funzione: tu ti aspetti che l'ordine degli argomenti sia significativo..., *tranne* che invece per i kwargs l'ordine improvvisamente *non* è significativo (perché sono implementati con un dizionario... naturalmente!).

Io trovo il discorso: ho implementato gli argomenti delle funzioni con un dizionario, ma il dizionario non va bene, quindi modifico tutti i dizionari completamente delirante.
Se per te questa linea di ragionamento e' sensata, non c'e' spazio per discutere. Abbiamo una visione di software engineering drammaticamente diversa e non c'e' nulla che nessuno possa dire o fare per convincermi che infilare un dettaglio implementativo in un'interfaccia di alto livello per via di alcuni use-case dell'implementazione sia una buona idea. Tutti i principi di software engineering che ho studiato, interiorizzato e applicato per mettere in piedi roba che appunto in piedi ci sta... bene, *tutti* (quelli rilevanti) dicono che e' una buona idea.

> Ora, ci sono alcuni corner-case dove questo potrebbe darti fastidio, da sempre... ma ovviamente il problema esplode letteralmente e diventa visibile a tutti nel momento in cui tu introduci una cosa come collections.OrderedDict, con l'idea di fare un favore alla gente... salvo che poi di colpo la gente si aspetta che OrderedDict(a=1, b=2) preservi le chiavi nell'ordine in cui ha inserito gli argomenti... solo che gli argomenti sono ancora un dict vecchio stile, non un OrderedDict a loro volta!

Questo e' un classico di esempio di quello che succede facendo piling di hack. OrderedDict e' una cosa apparentemente innocua, ma crea piu' problemi di quello che risolve.
Per intenderci, uno dei motivi per cui i named parameters sono considerati una feature (I disagree, sono un'altra idea apparentemente buona che quando fai sul serio di si ritorce contro) e' perche' riducono il costo cognitivo di ricordarci l'ordine (e.g., il classico copy(a, b) chi e' la destinazione? vs. copy(src=a, dst=b) che e' piu' facile da ricordare e da leggere). Quindi forse il punto e' che quando si parla di named argument quello che vuoi fare e' proprio "ignorare" l'ordine.

Un problemino dei named parameters? Lo vedi quando hai large code base e per esempio usi dei mock. E il problema e' che se qualcuno cambia copy(a, b) in copy(src=a, dst=b) il mock si rompe potenzialemente (sono curioso di sapere cosa succede adesso che abbiamo gli ordered dict --  spero che non abbiano fatto sta cazzata). E ancora una volta, finche' ti leggi e ti scrivi il codice non e' un problema.

Pero' *se* hai named arguments nel linguaggio, allora *anche* il nome dei parametri di una funzione (che normalmente non e' parte della signature -- solo il loro tipo lo e' -- ), beh, anche il nome e' parte della signature. E sai quanto tempo ho visto perdere su sta roba?

Come dicevo, questo non lo vedi se scrivi e leggi il tuo codice o lavori con due o tre persone. Quando lavori in contesti un po' piu' ampli, lo diventa. Oh, e' inevitabile eh... non dico nemmeno che i named arguments siano il male assoluto. Ci sono casi in cui mi sono stati comodi, figurati. Sono esempi di implicazioni non banali che derivano da scelte di design apparemente innocue. Perche' il problema non e' davvero che sia cosi': il problema e' che la maggior parte della gente non realizza immediatamente l'implicazione "qualcuno mi puo' chiamare per nome, quindi il nome e' ora intoccabile" -- vincolo che non ci sarebbe stato in altri contesti).

E non e' il peggio: appena cominci a mischiare "named arguments", "default arguments" e **kwargs, scopri che una serie di problemi che sarebbero potuti essere risolti direttamente dal compilatore, ora invece sono piu' complicati da individuare e debuggare.

Poi ovviamente, quando scrivo uno script di 500 righe... appunto in 500 righe faccio un *sacco* di cose. Pero' io avevo capito che
- Explicit is better than implicit.
- Complex is better than complicated.
- Flat is better than nested.
- Errors should never pass silently.
...

> E come si implementa, dietro le quinte, questa cosa? Ma semplicissimo: introducendo il nuovo dict di Raymond, che oltre a essere più veloce ha anche questo bellissimo side-effect di preservare l'ordine delle chiavi, che adesso ci viene proprio comodo.

Si ecco... e il fatto e' che per te questa e' una cosa buona. Per me e' delirio allo stato puro. Se a un meeting un'idea del genere venisse fuori, gli mangerebbero la faccia.

> Questa è una cattiva idea? E' una buona idea? Boh. Tanto ormai è andata.

Diciamo cosi'... credo semplicemente di avere outgrown python. Io ho in mente un mondo dove fa fatica e la gente che lo scrive ha in mente un altro mondo. Sono sicuro che Python vada benissimo per tanti la fuori. Sono convinto che per chi fa webapp con Django queste idee siano *davvero* comode. A me fa *davvero*  paura che nessuno si sia reso conto che questo modo di ragionare e' *pericoloso*.

Mi diverte scrivere in Python? Sempre. Ma mi fido sempre meno a farci roba seria.

Tra l'altro Python era il linguaggio con la macchina del tempo. Features apparentemente slegate che in modo emergente portavano qualcosa di elegante, semplice e potente. Adesso siamo arrivati a ficcare vincoli implementativi nelle strutture dati core perche' la gente non sa usare OrderedDict. Dal mio punto di vista non mi stupirei se fra qualche anno ci saranno __builtin__._COOKIE e __builtin__._POST.

> Ovviamente non c'è solo il caso di OrderedDict... per esempio, quando devi stampare gli argomenti di una funzione per l'utente, ti dà fastidio che non siano stampati nell'ordine "giusto".

Ma ci fate qualcosa di utile con sto Python? Cioe' il problema e' *stampare* gli argomenti o scrivere una funzione che faccia qualcosa di utile?

> Ma poi, in generale, oltre ai dizionari di kwargs, ci sono altri casi in cui un dizionario non-order preserving è una bella scocciatura. Caso tipico, la serializzazione, tutte le volte che hai bisogno di ottenere risultati riproducibili evitando diff spuri.

Si, tipo la serializzazione *se* uno non ha capito come organizzare  un layer di persistenza, non ha capito che l'uguaglianza non e' una proprieta' intrinseca, etc etc etc.
In altre parole: se stai scrivendo scriptoni tutto questo e' un problema. Se stai scrivendo roba piu' complicata... beh, no. Non lo e'. Quello che ci vorrebbe sarebbero buone API e buona separazione.

> Per questi casi il modulo json da sempre offre l'opzione di ordinare alfabeticamente le chiavi nell'output, ma ci sono casi in cui questo comunque non basta (per esempio quando lavori cross-locale e l'ordine di collazione potrebbe variare).

E questo e' un problema specifico dell'interfaccia troppo semplicistica dei vari moduli di serializzazione. E

Citazione
Ora, nella mia modesta esperienza, quando qualcuno produce un output in json, prima o poi a qualcun altro verrà in mente di usarlo, al di là degli scopi e dell'ambito in cui era stato concepito inizialmente. Quindi l'OP dovrebbe restare acutamente consapevole di stare introducento una semantica (l'ordine significativo delle chiavi del suo dizionario) che ha perfettamente senso nell'ambito Python (3.6+) del suo programma, ma che invece non trova corrispondenza nell'ambito del suo output json. Come minimo, dovrebbe documentare molto chiaramente la cosa. Come minimo.

Ovviamente.
« Ultima modifica: Ottobre 13, 2018, 12:42 da riko »

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Aggiungo: il problema e' che praticamente *tutti* i linguaggi fanno cacate quando vengono progettate le librerie di collections.
E se fai una cosa fatta *davvero* bene (ammesso che il linguaggio sia abbastanza espressivo) ti trovi con una cosa che potrebbe essere troppo complicata da usare (bene) dal grande pubblico. E piu' il linguaggio e' espressivo e piu' ti trovi con edge case che e' difficile da incastrare. E hai *sempre* il problema se rendere possibile una cosa poco efficiente (che e' un problema perche' porta a problemi inaspettati di performance) oppure dovere pagare overhead di sviluppo quando invece le performance non sono un problema.

Nel caso specifico di Python il problema e' che sono nate prima le implementazioni delle interfacce astratte (types, collections). Che e' anche uno dei problemi alla radice di quando hanno introdotto collections in Java (che ha portato a fare errori). In particolare, quello che hanno cazzato e' stato non avere fin da subito interfacce immutabili e interfacce mutabili. E *ovviamente* quando cominci a progettare la roba in questo modo e hai in giro un sacco di thread, scopri che anche questo porta problemi che puoi evitare solo introducendo struttura ben progettata, che e' complicato e non intuitivo. Oppure facendo quello che fa Rust (che porta in compenso una quantita' di complessita' che spesso mette in difficolta' *me*, per cui finisce che le cose sono anche progettate bene, ma piccoli errori da parte mia hanno un costo elevato, a meno di non essere *massimamente* generici, che porta i suoi problemi). Go lang ha evitato il problema della complicazione, e tutti a lamentarsi che e' troppo semplicistico (verissimo su certe cose).

Purtroppo il problema e' che non esiste una soluzione universale (a me nota) che abbia complessita' (di sviluppo, non algoritmica) che scali con i requisiti. Di solito ti trovi una piattaforma che e' troppo semplice per quando le cose si complicano o troppo complicata per quando le cose sono semplici. E se anche si riesce a tenere semplici le cose semplici, in genere mi sono accorto che progettare il codice intorno in modo che sia semplice finche' le cose sono semplici e poi si complica *gradualmente* quando i requisiti si complicano *non* e' banale. Specie se inserisci nell'equazione ingegneri esperti, ma con esperienza in *altre* tecnologie.

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 2.821
  • Punti reputazione: 9
    • Mostra profilo
Cavolo, il necroposting ti ha preso la mano eh?

Guarda, con molta franchezza: ci sono punti in cui non ci arrivo proprio a discutere con te, quindi non fingerò che mi sono chiare le cose che non mi sono chiare. Quindi poi ti chiedo.

Cominciamo a metterci d'accordo sul fatto che esistono vari livelli di esperienza e/o necessità a cui corrispondono diversi problemi che si scoprono. E magari si risolvono. Se quando sviluppi sistemi complessi in team grandi vien fuori che i named arguments sono una rogna (e non stento a crederlo da quel poco che ho visto/capito), allora a quel livello non userai i named arguments. Ma non me la sento di dare la colpa a Python perché ha i named arguments.
Poi certo, è anche una questione che le cattive abitudini acquisite "da piccoli" poi è più difficile perderle... ma insomma, questa è una questione didattica.

Ora, prendiamo il caso dei dizionari ordinati. Ti dici che OrderedDict mi sta simpatico, ma in realtà... boh, direi di no. Se voglio qualcosa che conservi l'ordine di inserimento uso una lista di tuple. Se voglio un dizionario ordinato... eh, non uso niente visto che python non ha treemap. Ma se uso un dizionario non mi interessa l'ordine. Ora, posso capire che un principiante non sappia quale struttura-dati usare. Ma se un principiante vuole usare OrderedDict, tutto sommato non mi disturba terribilmente. Più che altro sospetto che la *ragione* per cui lo usa sia sbagliata, e provo a investigare.
Io ho mai usato OrderdDict? Mah, sì, qualche volta. Per qualche aspetto secondario nel lavoro con le GUI, tipo produrre menu a partire da strutture-dati che li descrivono. In casi del genere un dizionario (eventualmente annidato per i sottomenu) va bene: peccato solo che *inoltre* le voci di un menu devono essere presentate con un ordine arbitrario, e il dizionario questo non lo supporta. E quindi, OrderedDict. E' un pattern sbagliato? Mah, onestamente non direi. Cioè sì, l'ordinamento è logica-di-presentazione, tutto il resto che metto nel dizionario è logica-di-business. Cioè sì, *potrei* fare un ulteriore layer solo per astrarre questa necessità. Ma andiamo, dai: un OrderedDict va benone per questo. Tutto il resto mi sembrerebbe una inutile complicazione.

E poi prendiamo il caso dei named arguments ordinati. Certo, *io* non ne ho mai sentito la necessità. E però non sono d'accordo con te:
>  il punto e' che quando si parla di named argument
> quello che vuoi fare e' proprio "ignorare" l'ordine.

Già, a meno che io non sia l'autore di un'estensione per un editor come questa https://packagecontrol.io/packages/AutoDocstring che ispeziona i parametri della funzione e produce automaticamente uno stub per la docstring, ecco ALLORA forse mi piacerebbe presentare anche i kwargs nell'ordine giusto, no? Eh. (Questa è anche una risposta alla tua domanda "ma ci fate qualcosa con python?, il problema è stampare gli argomenti?": di tanto in tanto...)
Oppure, se scrivessi metaclassi che costruiscono classi con named arguments: allora mi piacerebbe *un sacco* che la classe costruita avesse gli argomenti nell'ordine che dico io e non buttati a casaccio (e in modo diverso a ogni esecuzione)... o sbaglio? https://stackoverflow.com/questions/4459531/how-to-read-class-attributes-in-the-same-order-as-declared Nota che questo è proprio un esempio di "cosa che si rompe" (e si è sempre allegramente rotta, dall'alba dei tempi delle mataclassi) se *non* usi OrderedDict (cioè, se non ci sbatti il muso e poi impari a usare OrderedDict).


Dopo di che però iniziano le cose che non capisco più. Quando dici

> Un problemino dei named parameters?
> se qualcuno cambia copy(a, b) in copy(src=a, dst=b)
> il mock si rompe potenzialemente

Uh, che cosa vuol dire? Che se cambi la signature di una funzione potrebbe rompersi il suo mock? Beh sì, credo che sia vero *in generale*. Stai cambiano la signature di una funzione, caspita. Che tutto il resto intorno si rompa, beh un po' me lo aspetto.
Poi certo, se intendi dire: "non sembra che sto cambiando la signature, ma in python è così e questo causa confusione e fa perdere tempo", allora sì certo. La soluzione è non usare named arguments, hai ragione.

E poi aggiungi
> sono curioso di sapere cosa succede adesso che abbiamo gli ordered dict
> spero che non abbiano fatto sta cazzata

Non so proprio... penso che il mock che si rompeva prima si rompe anche ora (in particolare, se hai cambiato la signature non solo usando kwargs al posto di args, ma anche *cambiando* l'ordine degli argomenti), e quello che non si rompeva prima non si rompe neanche adesso... mi perdo qualcosa?
Semmai il contrario... se stavi già usando named arguments con i mock, è possibile che adesso le cose migliorino un pochino: https://mail.python.org/pipermail/python-ideas/2009-April/004165.html

Poi ci sono volte che non riesco a capire se esageri o se mi perdo qualcosa:

>> la serializzazione, tutte le volte che hai bisogno di ottenere
>> risultati riproducibili evitando diff spuri.
> Si, tipo la serializzazione *se* uno non ha capito come
> organizzare  un layer di persistenza

Maddai... davvero io non ne capisco molto, ma ultimamente vedo (per esempio) tutta questa ansia di produrre build deterministiche... Ora, il punto è che tutto quello che origina da un dizionario e finisce in qualche modo in un... file di testo (per dire... un output serializzato), non è deterministico. Non è bello, in certi casi, avere un dizionario ordinato? E non ci pensi più.
Oppure più banalmente (e in un altro campo), se faccio i test con doctest, che per forza di cose non può far altro che confrontare stringhe, mi piacerebbe eccome avere dei dizionari order-preserving, o no? E occhei, dopo "non usare json" anche "non usare doctest"... ma insomma, ce ne sono di cose implementate male in giro, tutte perché invece 'sti benedetti dizionari sono implementati bene, eh?


E poi ci sono delle volte in cui proprio non ho capito se sono io scemo o cosa:

> Io trovo il discorso: ho implementato gli argomenti
> delle funzioni con un dizionario, ma il dizionario
> non va bene, quindi modifico tutti i dizionari
> completamente delirante. Se per te questa linea di
> ragionamento e' sensata, non c'e' spazio per discutere.
> Abbiamo una visione di software engineering drammaticamente diversa

Ma no, dai. (Ok, qui potrei dirti che se i core devs di python - Guido compreso - hanno preso questa decisione, proprio deliranti magari non sono... ma sarebbe un falso argomento, si capisce). A parte che non hanno modificato i dizionari *per* ordinare i named arguments... diciamo che è stato un concorso di ragioni... ma anche se fosse così: non c'è qualcosa di sbagliato in quello che scrivi? Non è "modifico tutti i dizionari"... ma è "estendo" i dizionari, o no? Stiamo violando il principio di sostituzione qui? Tutto quel che potevi fare con i vecchi dizionari puoi farlo anche con i nuovi, o no? C'è un problema che non vedo?

> Adesso siamo arrivati a ficcare vincoli implementativi
> nelle strutture dati core perche' la gente non sa usare
> OrderedDict. Dal mio punto di vista non mi stupirei se
> fra qualche anno ci saranno __builtin__._COOKIE e __builtin__._POST.

Beh, no. Ci sono due aspetti separati qui: primo, i named arguments. Su questi, il vincolo implementativo non è stato introdotto adesso, ma proprio dall'inizio. Se li implementi con un dizionario, ti porti dietro i difettucci dei dizionari, e sei destinato a cambiare la loro semantica tutte le volte che cambi i dizionari. E' un peccato, certo, ma un peccato antico non recente. E da un punto di vista di design, ci sono pochi dubbi che i named arguments *dovrebbero* essere ordinati: a parte le applicazioni pratiche (metaclassi, etc), averli non-ordinati è un attrito cognitivo inutilmente crudele (e già il sistema dei parametri delle funzioni in Python non è proprio una perla di chiarezza, diciamolo). Quindi ben venga la patch tardiva.

Secondo, i dizionari. Certo, adesso è stato introdotto un vincolo implementativo: se in futuro, per dire, qualcuno dovesse fare un dizionario ancora più performante *ma* non order-preserving, non potrebbe più inserirlo dentro python. Occhei, sono d'accordo. Ma d'altra parte, l'evoluzione verso un dizionario order-preserving è nella natura delle cose, e sostanzialmente i core-devs di python sono sempre stati piuttosto d'accordo su questo, da Guido a Raymond stesso.
Ora so che adesso ti metti a smattare, ma a un certo punto un linguaggio vale anche perché è usato, e se la gente si aspetta i dizionari order-preserving, vuol dire che è una soluzione comoda in un certo numero di casi d'uso. Guarda javascript (ecco, adesso smatti: te l'avevo detto). Per anni la gente ha ciclato su Object aspettandosi che fosse order-preserving. Adesso finalmente hanno Map: il risultato è che tutti usano Map nello stesso modo in cui (speravano di) usare Object. Per anni quelli di Chrome sono andati avanti a litigare con gli sviluppatori su questo (https://bugs.chromium.org/p/v8/issues/detail?id=164#c83 ma tutto il thread...), mentre mettevano patch per sistemare piccoli bachetti, cose da nulla capisci... tipo che *il feed di Facebook* su Chrome non veniva ordinato giusto... Ma se alla fine pure i devs di Facebook sono dei niubbi che non sanno usare le strutture-dati corrette, non so, qualcosa di sbagliato ci sarà. Alla fine, devi leggere l'ultimo commento del thread: "Previously, our customized JSON parser generated a separated array of the properties on the insertion order on a Object. Now, we switched to Map everywhere". Ecco fatto, tanto ci voleva, problema risolto, passiamo ad altro.

Ora, anche se in linea di principio vedo le tue obiezioni (non tutte, e non tutte sono in grado di capirle), a lato pratico non credo che i core devs di Python in questo pasticcio se la siano cavata malissimo tenute conto delle condizioni al contorno. Non hai poi tutte quelle collections nella libreria standard; se i dizionari sono l'unico mapping che hai, la gente li usa e si aspetta che siano order-preserving; che i dizionari siano order-preserving, del resto, è probabilmente una buona cosa nel 2018; oltre tutto li hai usati per anni per implementare i named arguments in modo bizzarro bordeggiante nel fallato. Dato questo, la soluzione proposta non mi sembra che rompa codice preesistente, è più performante... e sì: hai cambiato sottilmente la semantica di un builtin. Per un linguaggio che ha vissuto il dramma di py2/py3 forse non è la cosa più grave.

Poi certo, quando vedrò __builtins__.COOKIE mi verrà il sospetto che avevi ragione tu fin dal principio.

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.447
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Re:[RISOLTO]Dizionario->Json->Dizionario, mantenere l'ordine delle chiavi
« Risposta #9 il: Novembre 14, 2018, 21:16 »
Cavolo, il necroposting ti ha preso la mano eh?

Macche' necro... la risposta e' stata scritta alla massima velocita' che potevo...

> Cominciamo a metterci d'accordo sul fatto che esistono vari livelli di esperienza e/o necessità a cui corrispondono diversi problemi che si scoprono. E magari si risolvono. Se quando sviluppi sistemi complessi in team grandi vien fuori che i named arguments sono una rogna (e non stento a crederlo da quel poco che ho visto/capito), allora a quel livello non userai i named arguments. Ma non me la sento di dare la colpa a Python perché ha i named arguments.

Non sono stato chiaro. In molti linguaggi comuni il *nome* degli argomenti non e' parte della signature del metodo (e in Python stesso e' irrilevante quando usati in modo posizionale).
Il problema e' che *tutti* gli argomenti possono essere chiamati per nome il che vuole dire che in Python il *nome* degli argomenti e' *sempre* parte della signature del metodo.
Quindi un cambiamento apparentemente innocuo come copy(src, dst) nel piu' esteso (a volte, coding guidelines etcc etc etc) copy(source, destination) andrebbe trattato come un braking api change ma *non appare* come tale.

Io credo che anche molti sviluppatori che sono perfettamente consci che le loro funzioni possono venire chiamate con parametri per nome o per posizione non realizzino che quello in realta' e' un breaking change.
Se invece ogni argomento potesse essere chiamato *solo* per nome o per posizione il problema non sussisterebbe.

Ora non e' un dramma (ma ora rifletti che invece quando scrivi in C questo comportamento dipende se usi PyArg_ParseTupleAndKeywords oppure solo PyArg_ParseTuple).

Il mio punto e' che tutta la parte intorno ai named arguments ha un po' di problemi di suo e che sembra un'idea molto buona finche' uno non si ferma a riflettere.

> . Cioè sì, *potrei* fare un ulteriore layer solo per astrarre questa necessità. Ma andiamo, dai: un OrderedDict va benone per questo. Tutto il resto mi sembrerebbe una inutile complicazione.

Oh, ma figurati, certo. Ci sono usi occasionali per OrderedDict. Ora, detto fra noi... sarebbe sempre il mestiere di un sorted dict, a volerla dire tutta. Poi vabbe' ti hanno dato OrderedDict e lo fai con quello.
Io pero' avevo tanto in mente tutta sta storia delle serializzazioni...

E poi prendiamo il caso dei named arguments ordinati. Certo, *io* non ne ho mai sentito la necessità. E però non sono d'accordo con te:
>  il punto e' che quando si parla di named argument
> quello che vuoi fare e' proprio "ignorare" l'ordine.

> Già, a meno che io non sia l'autore di un'estensione per un editor come questa https://packagecontrol.io/packages/AutoDocstring che ispeziona i parametri della funzione e produce automaticamente uno stub per la docstring, ecco ALLORA forse mi piacerebbe presentare anche i kwargs nell'ordine giusto, no? Eh. (Questa è anche una risposta alla tua domanda "ma ci fate qualcosa con python?, il problema è stampare gli argomenti?": di tanto in tanto...)

E ovviamente, visto che stai scrivendo un editor di testo, cosa cavolo ti impedisce di andare a parsarti il sorgente e mettere gli argomenti nell'ordine giusto?
E scusami... vorrei vederci chiaro. Tipo il parser builtin con Python *ovviamente* conserva l'ordine degli argomenti della funzione.

In [3]: ast.parse('foo(bar="bar", baz="baz")')
Out[3]: <_ast.Module at 0x106864278>

In [4]: t = _

In [5]: ast.dump(t)
Out[5]: "Module(body=[Expr(value=Call(func=Name(id='foo', ctx=Load()), args=[], keywords=[keyword(arg='bar', value=Str(s='bar')), keyword(arg='baz', value=Str(s='baz'))]))])"

In [6]: ast.dump(ast.parse('foo(baz="bar", bar="baz")'))
Out[6]: "Module(body=[Expr(value=Call(func=Name(id='foo', ctx=Load()), args=[], keywords=[keyword(arg='baz', value=Str(s='bar')), keyword(arg='bar', value=Str(s='baz'))]))])"
[/quote]

Poi per ovvi motivi non vuoi usare quella li, e infatti il tizio secondo me se l'e' scritta. E allora se la scriva giusta? :)


> https://stackoverflow.com/questions/4459531/how-to-read-class-attributes-in-the-same-order-as-declared Nota che questo è proprio un esempio di "cosa che si rompe" (e si è sempre allegramente rotta, dall'alba dei tempi delle mataclassi) se *non* usi OrderedDict (cioè, se non ci sbatti il muso e poi impari a usare OrderedDict).

E ancora una volta, tutta questa roba si fa con un sorted dict, se vuoi. Un treemap, come lo chiamavamo prima.

[quote]
> Un problemino dei named parameters?
> se qualcuno cambia copy(a, b) in copy(src=a, dst=b)
> il mock si rompe potenzialemente

Uh, che cosa vuol dire? Che se cambi la signature di una funzione potrebbe rompersi il suo mock? Beh sì, credo che sia vero *in generale*. Stai cambiano la signature di una funzione, caspita. Che tutto il resto intorno si rompa, beh un po' me lo aspetto.
[/quote]

Ah, pensavo fosse piu' chiaro. No, quello e' il call site. Ovvero, se in un punto interno del mio codice cambio il modo in cui chiamo copy, mock si rompe. Ora voglio essere chiaro, non e' che il fatto che mock si rompa e' un problema di Python. Il problema e' che questa cosa dei named parameter introduce *tante* piccole ambiguita'. Quindi se stai usando mock devi stare attento a come fai le asserzioni. Mock decide di essere esplicito (correttamnte secondo zen), ma il punto e' proprio che sotto molti punti di vista le chiamate copy(a,b) e copy(src=a, dst=b) sono la stessa cosa. Ma non sono *davvero* la stessa cosa. E' un dramma? No. E' una cosa che ti fa perdere un po' di tempo di tanto in tanto.

[pythoncode]In [8]: m = mock.MagicMock()

In [9]: m(a=1, b=1)
Out[9]: <MagicMock name='mock()' id='4404144336'>

In [10]: m.assert_called_once_with(1, 2)[/pythoncode]

> Poi certo, se intendi dire: "non sembra che sto cambiando la signature, ma in python è così e questo causa confusione e fa perdere tempo", allora sì certo. La soluzione è non usare named arguments, hai ragione.

E sfortunatamente non e' cosi'. Perche' l'uso (o no) dipende da chi mi chiama, non da me. Che io sappia l'unico modo in pure python di impedire che ti chiamino con named argument e' fare qualcosa come (o varianti sul tema) -- ma ti perdi tanto:

In [11]: def f(*args):
    ...:     a, b = args


> Maddai... davvero io non ne capisco molto, ma ultimamente vedo (per esempio) tutta questa ansia di produrre build deterministiche...

Non capisco di cosa parli... ma potrebbbe essere che semplicemente tanti dei niubbi che prima finivano in altri lidi ora arrivano qui?

> Ora, il punto è che tutto quello che origina da un dizionario e finisce in qualche modo in un... file di testo (per dire... un output serializzato), non è deterministico.

Boh... se mi serve un certo ordine usero' quell'ordine. Se non mi serve, non lo usero'.

> Non è bello, in certi casi, avere un dizionario ordinato? E non ci pensi più.

Non ci pensi piu'? Abbiamo deciso che l'ordine di output e' importante (se no non lo stiamo facendo).
Per cui ... o speriamo che il dizionario prodotto in un altro punto del codice, magari giorni prima, abbia ancora l'ordine che vogliamo in output oppure lo dobbiamo... riordinare. Non vogliamo farlo? Bene: abbiamo fatto si che un vincolo su uno specifico caso di serializzazione si propaghi fino alla creazione dell'oggetto. E preghiamo forte che non dobbiamo serializzarlo con un altro ordine da un'altra parte. Se no siamo fottuti (oh, certo, possiamo deciedere che uno dei due e' di prima classe... poi i tiZi che scrivono gli altri use-case si chiedono perche' a loro e' andata male).

Ah... oppure ce ne fottiamo e riordiniamo il dizionario ordinato (o ne creiamo uno nuovo con l'ordine giusto). E allora chissene frega dell'ordine del dizionario: ordinero' le chiavi mano mano che le tiro fuori, no?

Il punto e' che se per te e' ok che un vincolo su come devi serializzare una struttura determina come la devo creare, abbiamo visioni su quello che sia un buon design non compatibili. E per me e' talmente ovvio che e' una cattiva idea  he non riesco nemmeno a spiegarti perche' (cioe', posso nominare principi, pattern... ma e' tutta roba nota che sono sicuro tu sappia: se non ti hanno convinto quegli autori, non ho l'arroganza di riuscirti).

Non e' che mi dia fastidio il dizionario ordinato. Sono sicuro che troviamo use case in cui davvero e' la cosa comoda (magari perche' quello che ci interessa e' appunto l'ordine di inserimento). Il problema e' che la maggior parte degli use-case che sento per lui sono use-case in cui viene abusato e di fatto quello che interessa e' che abbia un certo ordine.
In particolare, crei una dipendenza fra come costruisci l'oggetto e come lo visiti (perche' stai spingendo questa logica nel concetto, di per se agnostico, di mappa).

> Oppure più banalmente (e in un altro campo), se faccio i test con doctest, che per forza di cose non può far altro che confrontare stringhe, mi piacerebbe eccome avere dei dizionari order-preserving, o no?

Ok. Dimmi quando questa cosa ha fine. Perche' qui il pattern e' giustifichiamo cattive idee mostrando che se ho altre cattive idee queste diventano piu' semplici. Poi mi si chiede perche' gradualmente mi allontano da Python. :)

Cioe' doctest grandioso. Il suo scopo principe per cui e' davvero grandioso e' che risolve brillantemente il problema in cui ho del codice nella documentazione e lo devo tenere valido. E' egregio e se non ci fosse il mondo sarebbe piu' complicato. Ovviamente *puoi* pensare di usarlo per i test. Tipo se hai una funzione di 10 righe con un happy path e due failure mode. E lo fai quando l'alternativa sarebbe "no test".

Ma ancora uan volta... a me non capita di scrivere funzioni di 10 righe che vivono in isolamento (tipo prendono due interi e una stringa). Insomma... nella maggior parte dei casi voglio pytest. E grazie: contestalo. Tutti stronzi quelli che hanno scritto pytest perche' tanto basta doctest, no? Ma oggettivamente, basta un file pieno di assert una dietro l'altro, no? "basta".

E ora vogliamo ordinare i dizionari per rende piu' facile un'anti-pattern: quello di verificare le strutture dati transformandole in stringhe e poi contollando che sono uguali. Di questo stiamo parlando alla fine... :)

[quote]
E poi ci sono delle volte in cui proprio non ho capito se sono io scemo o cosa:

> Io trovo il discorso: ho implementato gli argomenti
> delle funzioni con un dizionario, ma il dizionario
> non va bene, quindi modifico tutti i dizionari
> completamente delirante. Se per te questa linea di
> ragionamento e' sensata, non c'e' spazio per discutere.
> Abbiamo una visione di software engineering drammaticamente diversa

> Ma no, dai. (Ok, qui potrei dirti che se i core devs di python - Guido compreso - hanno preso questa decisione, proprio deliranti magari non sono... ma sarebbe un falso argomento, si capisce).
[/quote]

Puoi perfino prendere una decisione giusta per motivi deliranti. O prendere una decisione sbagliata per ottimi motivi.
Specificamente ha senso ottimizzare i dizionari? Si, sempre. O meglio: ha senso ottimizzare le cose sul path delle chiamate dei metodi? Certo.
In certi contesti ci si pone perfino il problema di non avere metodi virtuali perche' sono lenti (e.g., C++, Rust). Java minimizza il problema per via del JIT. Python paga tutto. Vogliamo fargli pagare un po' meno. Buona idea. "Tutti" chiamano metodi. Non e' sempre critico, ma e' un buon posto dove ottimizzare

> A parte che non hanno modificato i dizionari *per* ordinare i named arguments... diciamo che è stato un concorso di ragioni... ma anche se fosse così: non c'è qualcosa di sbagliato in quello che scrivi?

Boh, forse. Ovviamente se pensassi di si, non lo farei. ;)
E no, penso che forzare tutti i dizionari di Python ad essere ordered dict per questa cosa non e' una buona idea.

> Non è "modifico tutti i dizionari"... ma è "estendo" i dizionari, o no? Stiamo violando il principio di sostituzione qui? Tutto quel che potevi fare con i vecchi dizionari puoi farlo anche con i nuovi, o no? C'è un problema che non vedo?

Il mio problema non e' che scelgano un comportamento fra i tanti implementation defined. Il mio problema e' che questo diventi un vincolo. E capisco: nel momento in cui per alcuni anni tutti sono abituati che i dizionari sono ordinati (e la gente ci farebbe affidamento), non sarebbe comunque possibile tornare indietro facilmente, anche senza renderlo ufficiale.

Forse verrebbe da dire che il fatto che tutto il macchinario di OOP di Python si fondi sulla stessa struttura dati user-facing che usiamo in 27 altri modi e posti non e' la migliore idea (diverso pattern d'uso?). E ovviamente poi siccome questa cosa del dizionario e' molto leaky come abstraction questa cosa potrebbe essere inevitabile.

Diciamo che arriviamo al punto critico: ho scritto Python per piu' di 15 anni. Quello che si fa con un computer pero' cambia. Anche le cose in cui una piattaforma e' forte e debole cambiano con gli anni (e.g., il GIL non era davvero un problema su macchine con 2 core, adesso e' un dramma e si s

[quote]
Beh, no. Ci sono due aspetti separati qui: primo, i named arguments. Su questi, il vincolo implementativo non è stato introdotto adesso, ma proprio dall'inizio. Se li implementi con un dizionario, ti porti dietro i difettucci dei dizionari, e sei destinato a cambiare la loro semantica tutte le volte che cambi i dizionari. E' un peccato, certo, ma un peccato antico non recente. E da un punto di vista di design, ci sono pochi dubbi che i named arguments *dovrebbero* essere ordinati: a parte le applicazioni pratiche (metaclassi, etc), averli non-ordinati è un attrito cognitivo inutilmente crudele (e già il sistema dei parametri delle funzioni in Python non è proprio una perla di chiarezza, diciamolo). Quindi ben venga la patch tardiva.
[/quote]

E anche qui... il problema in effetti sono i named arguments. Perche' mentre viene istintivo considerarne l'ordine quando si definisce la funzione (anche perche' e' sempre possibile chiamarla senza i nomi), invece nei punti di chiamata l'ordinamento non e' rilevante (e meno male, visto che e' uno dei "vantaggi" che i named arguments portano).

Secondo, i dizionari. Certo, adesso è stato introdotto un vincolo implementativo: se in futuro, per dire, qualcuno dovesse fare un dizionario ancora più performante *ma* non order-preserving, non potrebbe più inserirlo dentro python. Occhei, sono d'accordo.

>  Ma d'altra parte, l'evoluzione verso un dizionario order-preserving è nella natura delle cose,

No. Non e' nella natura delle cose. E' una scelta come un'altra. Quella che fa Ruby, per esempio. Go prende la scelta opposta: *randomizza* l'ordine di enumerazione. A me pare un po' estremo, ma certamente evita il problema che qualcuno possa pensare per piu' di 10 nanosecondi che una mappa e' una struttura ordinata.

> Ora so che adesso ti metti a smattare, ma a un certo punto un linguaggio vale anche perché è usato, e se la gente si aspetta i dizionari order-preserving, vuol dire che è una soluzione comoda in un certo numero di casi d'uso.

Tutt'altro. Questo e' il punto chiave (non sono d'accordo sul "vale se e' usato", da persona che pensava che python valesse quando non era usato). E non sono d'accordo nemmeno che sia una soluzione comoda. Tutti i casi d'uso presentati si risolvono con un sorted dict. Il punto e' che questo e' un testamento. Si sono accorti che con l'utenza odierna di Python l'attrito cognitivo era troppo. E *fanno* bene a farlo, se la situazione e' questa. Pero' sarebbe anche molto triste. O forse nemmeno. E' triste perche' dopo tutto sapere qualcosa di strutture dati non serve ad una mazza. Cioe', serve di tanto in tanto, figurati. Ma non particolarmente piu' di altre cose.

E si, mi sono amminchiato su questa cosa. Python 3.7 e' davvero meglio di 2.7.  E oggettivamente nella pratica non mi cambia *nulla*. Mi avrebbe fatto piu' piacere un sorted dict che mi avrebbee dato cose che dict non mi da? Si. Certo. Me lo posso sempre scrivere, voglio dire. :)

[quote]
Guarda javascript (ecco, adesso smatti: te l'avevo detto). Per anni la gente ha ciclato su Object aspettandosi che fosse order-preserving. Adesso finalmente hanno Map: il risultato è che tutti usano Map nello stesso modo in cui (speravano di) usare Object. Per anni quelli di Chrome sono andati avanti a litigare con gli sviluppatori su questo (https://bugs.chromium.org/p/v8/issues/detail?id=164#c83 ma tutto il thread...), mentre mettevano patch per sistemare piccoli bachetti, cose da nulla capisci... tipo che *il feed di Facebook* su Chrome non veniva ordinato giusto... Ma se alla fine pure i devs di Facebook sono dei niubbi che non sanno usare le strutture-dati corrette, non so, qualcosa di sbagliato ci sarà. Alla fine, devi leggere l'ultimo commento del thread: "Previously, our customized JSON parser generated a separated array of the properties on the insertion order on a Object. Now, we switched to Map everywhere". Ecco fatto, tanto ci voleva, problema risolto, passiamo ad altro.
[/quote]

Stiamo sempre parlando di cambiare l'implementazione per fixare i bachi alla gente. Perche' i bachi sono bachi. E' una buona idea? Boh. Nel breve tempo sembra una buona idea. Io ho paura delle ripercussioni a medio termine. Ci sta anche che sia davvero il trend dei linguaggi con tipizzazione dinamica... per i quali oggettivamente il concectto di array associativo ha similiarita' notevoli con alcune proprieta' core del concetto di oggetto. Forse e' davvero una buona idea.  Pero' c'e' il caso he si sposti solo il problema.

Hai sicuramente reso complicata la vita a tutti quelli che devono supportare Python < 3.5. Ovviamente se hanno best practice no, non gli hai creato un gran problema. Se pero' non e' cosi'....
E va bene. Potrebbe anche essere un trade off accettabile.