Perché i dizionari Python non sono reversibili per Python3.7?


11

A partire dalla 3.7, i dizionari Python standard sono garantiti per mantenere l'ordine di inserimento. (*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

In altre parole, le chiavi del dict sono mantenute in un ordine rigoroso. In linea di principio, ciò consentirebbe alle chiavi di essere reversibili. Tuttavia, nessuna delle seguenti funzioni:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

Domande: qual è il ragionamento alla base di questo comportamento? Perché i dadi non sono stati resi reversibili? Ci sono piani per cambiare questo comportamento in futuro?

La soluzione ovviamente funziona:

for k in reversed(list(d.keys())): 
    print(k)

(*) È un dato di fatto, questo è già il caso di installazioni tipiche di Python 3.6, come discusso in questo post .


Aggiornamento : a partire da python 3.8 dicts sono effettivamente reversibili. La risposta accettata si riferisce alla discussione tra Guido e altri sviluppatori principali che ha portato a questa decisione. In breve, hanno ponderato la coerenza linguistica rispetto agli sforzi di implementazione e ai benefici effettivi per gli utenti.

Risposte:


5

Dai documenti :

invertito ( seq )

Restituisce un contrario iterator. seq deve essere un oggetto che ha un __reversed__()metodo o supporta il protocollo di sequenza (il __len__()metodo e il __getitem__()metodo con argomenti interi che iniziano da 0).

Un dictoggetto non implementa __reversed__. Implementa entrambi questi ultimi metodi. Tuttavia, __getitem__accetta le chiavi come argomenti, anziché come numeri interi (a partire da 0).

Quanto al perché, questo è già stato suggerito e discusso qui .

MODIFICARE:

Queste citazioni provengono dalla mailing list di Python-Dev (thread "Aggiungi metodi __reversed__ per dict", iniziato il 25. 05. 18), inizierò con gli argomenti "concettuali", il primo è di Antoine Pitrou:

Non vale nulla che OrderedDict supporta già reversed (). L'argomento potrebbe andare in entrambi i modi:

  1. dict è simile a OrderedDict al giorno d'oggi, quindi dovrebbe supportare anche reverse ();

  2. puoi usare OrderedDict per segnalare esplicitamente che tieni all'ordine; non c'è bisogno di aggiungere nulla a dict.

Il mio pensiero è che l'ordine di inserimento garantito per i dadi regolari sia nuovo di zecca, quindi ci vorrà un po 'di tempo prima che la nozione si stabilizzi e diventi parte del pensiero quotidiano sui dadi. Una volta che ciò accade, è probabilmente inevitabile che emergano casi d'uso e che __reversed__ verrà aggiunto ad un certo punto. L'implementazione sembra semplice e non è un gran salto concettuale aspettarsi che una raccolta ordinata finita sia reversibile.

Seguita dalla risposta di Raymond Hettinger:

Dato che i dicts ora tengono traccia dell'ordine di inserzione, sembra ragionevole voler conoscere gli inserimenti più recenti (ovvero passare in rassegna le attività aggiunte più di recente in un task dict). Altri possibili casi d'uso corrisponderanno probabilmente a come utilizziamo il comando di coda Unix.

Se si presentano questi casi d'uso, sarebbe bello che __reversed__ fosse già supportato in modo che le persone non fossero tentate di implementare una brutta soluzione usando le chiamate popitem () seguite da reinserzioni.

La principale preoccupazione espressa nella mailing list era che ciò avrebbe aggiunto troppa quantità o ridotto l'efficienza della memoria (dovendo avere elenchi doppiamente collegati invece di quelli singolarmente collegati) in almeno alcune implementazioni, ecco la citazione di Inada Naoki dal bug tracker di Python ( numero 33462 ):

"Avere un ordine" non significa "reversibile". Ad esempio, viene ordinato un singolo elenco collegato, ma non reversibile.

Sebbene l'implementazione di CPython sia in grado di fornire in modo efficiente __reverse__, l'aggiunta __reverse__implica che tutta l' implementazione di Python dovrebbe fornirla. Ad esempio, alcune implementazioni di Python potrebbero essere in grado di implementare dict con hashmap + singolo elenco collegato. Se __reverse__viene aggiunto, non è più possibile.

Torna alla mailing list, ecco gli ultimi due messaggi (entrambi pubblicati l'08.06.2018). Il primo è di Michael Selik:

Ho ragione nel dire che il consenso è +1 per l'inclusione nella v3.8?

L'ultimo punto nel thread è stato INADA Naoki che ricerca varie implementazioni e decide che è OK includere questa funzione in 3.8. A quanto ho capito, Guido era d'accordo con il consiglio di INADA di attendere l'implementazione di MicroPython della v3.7. Da quando INADA ha cambiato idea, immagino sia tutto a favore?

Concludendo con il messaggio di Guido van Rossum:

Mi sembra giusto. Avremo quindi avuto due versioni in cui questo era il caso:

  • 3.6 dove la conservazione dell'ordine è stata implementata in CPython ma nelle specifiche del linguaggio

  • 3.7 dove è stato aggiunto anche alle specifiche della lingua

Come notato nell'altra risposta e nei commenti, reversed()è supportato sia per i dicts che per i dictviews dalla versione 3.8 (14.10.2018).


5
La tua seconda citazione sembra una selezione di bias da quel thread. Il consenso sembra essere che la funzionalità verrà aggiunta in 3.8. Anche prima di Python 3.7 non era semplicemente possibile ordinare un dictoggetto normale (almeno non garantito dalla lingua), quindi reversednon aveva senso
FlyingTeller

Non ho un cane in combattimento, ho appena citato la sua prima risposta. Ma hai ragione, punto ben preso - ho rimosso la citazione.
gst

1
Grazie. Il thread di discussione del python-dev era rivelatore. È un dato di fatto, la funzionalità è già stata implementata in Python 3.8, che è stato rilasciato solo due giorni fa (14 ottobre 2019).
Normanio


La citazione di documenti non è utile, pone solo la domanda successiva "quindi, perché il tipo dict non implementa __reversed__"? Il collegamento python-dev ha contenuti utili, ma le parti pertinenti dovrebbero essere riprodotte direttamente nella risposta (poiché tali collegamenti fuori sede tendono a marcire).
mercoledì


1

Aggiornamento per Python 3.8

Dict e dictvview sono ora iterabili in ordine di inserzione inversa usando reversed ()

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>

Questa risposta fornisce qualcosa di nuovo che non è già coperto da altre risposte, commenti o dalla domanda stessa?
normanio

@normanius Ha un piccolo esempio di codice visivo, potrebbe essere utile per il browser veloce
jamylak
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.