Track
/
Python
Python
/
Esercizi
/
Gestione dei Mecha Munch
Gestione dei Mecha Munch

Gestione dei Mecha Munch

Esercizio di apprendimento

Introduzione

La classe dict di Python mette a disposizione tanti metodi utili per lavorare con i dizionari. Alcuni li abbiamo già incontrati nel concetto dedicato ai dict. Qui ne vediamo altri, insieme ad alcune tecniche per iterare sui dizionari e modificarli.

Usa setdefault() per un inserimento senza errori

Nel concetto sui dizionari abbiamo già visto che .get(key, <default value>) restituisce un value esistente, oppure il default value se una key non è presente nel dizionario, evitando così un KeyError. Funziona bene quando preferisci evitare una gestione degli errori extra, ma non puoi dare per scontato che la key che cerchi sia presente.

Per un'operazione di inserimento altrettanto «sicura» (senza KeyError), esiste il metodo .setdefault(key, <default value>). setdefault(key, <default value>) restituisce il value se la key è presente nel dizionario. Se la key non è presente, inserisce la coppia (key, default value) e restituisce il default value da usare.

>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}

# Looking for the value associated with key "Rock Brown".
# The key does not exist, so it is added with the default value, and the value is returned.
>>> palette_I.setdefault('Rock Brown', '#694605')
'#694605'

# The (key, default value) pair has now been added to the dictionary.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}

Usa fromkeys() per popolare un dizionario da un iterabile

Per popolare rapidamente un dizionario con varie keys e valori predefiniti, il metodo di classe fromkeys(iterable, <default value>) itera su un iterabile di keys e crea un nuovo dict. Tutti i values vengono impostati al default value fornito:

>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')

{'Grassy Green': 'fill in hex color here',
 'Purple Mountains Majesty': 'fill in hex color here',
 'Misty Mountain Pink': 'fill in hex color here'}

Iterare sulle voci di un dizionario tramite le viste

I metodi .keys(), .values() e .items() restituiscono delle viste iterabili di un dizionario.

Queste viste possono essere usate per scorrere facilmente le voci senza modificarle. Le viste sono anche dinamiche: quando i dati sottostanti del dizionario cambiano, il view object associato riflette la modifica:

>>> palette_I = {'Grassy Green': '#9bc400',
                 'Purple Mountains Majesty': '#8076a3',
                  'Misty Mountain Pink': '#f9c5bd'}

# Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])

# Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])

# Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])

# Views are dynamic.  Changing values in the dict changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
>>> palette_I['Deep Red'] = '#932432'

>>> palette_I.values()
dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])

>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])

>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])

Ancora su .keys(), .values() e .items()

In Python 3.7+, i dict conservano l'ordine in cui le voci vengono inserite, permettendo un'iterazione First-in, First-out (FIFO) quando usi .keys(), .values() o .items().

In Python 3.8+, le viste sono anche reversibili. Questo permette di iterare su keys, values o coppie (key, value) in ordine Last-in, First-out (LIFO) usando reversed(<dict>.keys()), reversed(<dict>.values()) o reversed(<dict>.items()):

>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}

# Iterating in insertion order (First in, first out)
>>> for item in palette_II.items():
...     print(item)
...
('Factory Stone Purple', '#7c677f')
('Green Treeline', '#478559')
('Purple baseline', '#161748')


# Iterating in the reverse direction. (Last in, first out)
>>> for item in reversed(palette_II.items()):
...    print (item)
...
('Purple baseline', '#161748')
('Green Treeline', '#478559')
('Factory Stone Purple', '#7c677f')

Ordinare un dizionario

I dizionari non hanno un metodo di ordinamento integrato. Tuttavia, è possibile ordinare una vista di un dicts usando la funzione integrata sorted() con dict.items(). La vista ordinata può poi essere usata per creare un nuovo dizionario. Come per l'iterazione, l'ordinamento predefinito avviene sulle keys del dizionario.

# Default ordering for a dictionary is insertion order (First in, first out).
>>> color_palette = {'Grassy Green': '#9bc400', 
                    'Purple Mountains Majesty': '#8076a3', 
                    'Misty Mountain Pink': '#f9c5bd', 
                    'Factory Stone Purple': '#7c677f', 
                    'Green Treeline': '#478559', 
                    'Purple baseline': '#161748'}
 
 
# The default sort order for a dictionary uses the keys.
>>> sorted_palette = dict(sorted(color_palette.items()))
>>> sorted_palette
{'Factory Stone Purple': '#7c677f',
 'Grassy Green': '#9bc400',
 'Green Treeline': '#478559',
 'Misty Mountain Pink': '#f9c5bd',
 'Purple Mountains Majesty': '#8076a3',
 'Purple baseline': '#161748'}

Combinare dizionari con .update()

<dict_one>.update(<dict_two>) può essere usato per combinare due dizionari. Questo metodo prende le coppie (key,value) di <dict_two> e le scrive in <dict_one>:

>>> palette_I = {'Grassy Green': '#9bc400',
                 'Purple Mountains Majesty': '#8076a3',
                  'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f',
                  'Green Treeline': '#478559',
                  'Purple Baseline': '#161748'}

>>> palette_I.update(palette_II)

# Note that new items from palette_II are added.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}

Dove le key dei due dizionari si sovrappongono, il value in dict_one viene sovrascritto dal value corrispondente di dict_two:

>>> palette_I =   {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 
                   'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
                   'Misty Mountain Pink': (249, 197, 189)}
>>> palette_I.update(palette_III)

# Overlapping values in palette_I are replaced with values from palette_III
>>> palette_I
{'Grassy Green': (155, 196, 0),
  'Purple Mountains Majesty': (128, 118, 163), 
  'Misty Mountain Pink': (249, 197, 189), 
  'Factory Stone Purple': '#7c677f', 
  'Green Treeline': '#478559', 'Purple baseline': '#161748'}

Unire o aggiornare dizionari con gli operatori union (| e |=)

Python 3.9 introduce un modo diverso di unire i dicts: gli operatori union. dict_one | dict_two crea un nuovo dizionario, formato dalle coppie (key, value) di dict_one e dict_two. Quando i due dizionari condividono delle key, i valori di dict_two hanno la precedenza.

>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> new_dict = palette_I | palette_II
>>> new_dict
...
{'Grassy Green': '#9bc400',
 'Purple Mountains Majesty': '#8076a3',
 'Misty Mountain Pink': '#f9c5bd',
 'Factory Stone Purple': '#7c677f',
 'Green Treeline': '#478559',
 'Purple baseline': '#161748'}

dict_one |= other si comporta in modo simile a <dict_one>.update(<other>), ma in questo caso other può essere sia un dicts sia un iterabile di coppie (key, value):

>>> palette_III = {'Grassy Green': (155, 196, 0),
                   'Purple Mountains Majesty': (128, 118, 163),
                   'Misty Mountain Pink': (249, 197, 189)}
>>> new_dict |= palette_III
>>> new_dict
...
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}

Per una spiegazione dettagliata dei dizionari e dei metodi per lavorarci, il tutorial ufficiale e il riferimento ufficiale della libreria sono ottimi punti di partenza.

Anche Real Python e Finxter hanno articoli molto approfonditi sui dizionari di Python.

Istruzioni

Mecha Munch™, un'azienda di automazione per la spesa, ti ha appena assunto per lavorare sulla sua app di ordini. Il tuo team ha il compito di realizzare un MVP (prodotto minimo funzionante) che gestisca tutte le attività di base del carrello, permettendo agli utenti di aggiungere, rimuovere e ordinare i propri ordini di generi alimentari. Per fortuna, un altro team si occupa di tutte le funzioni relative al denaro e alla cassa!

1. Aggiungere articoli al carrello dell'utente

L'MVP dovrebbe permettere all'utente di aggiungere articoli al proprio carrello. Può trattarsi di un singolo articolo o di più articoli contemporaneamente. Dato che si tratta di un MVP, la quantità degli articoli è indicata dalle ripetizioni. Se un utente vuole aggiungere 2 arance, «Oranges» comparirà due volte nell'iterabile di input. Se l'utente ha già l'articolo nel carrello, la quantità nel carrello va aumentata di 1. Se l'articolo è nuovo per il carrello, va aggiunto con una quantità di 1.

Crea la funzione add_item(<current_cart>, <items_to_add>) che prende come argomenti un dizionario carrello e un qualsiasi iterabile simile a una lista di articoli da aggiungere. Dovrebbe restituire un nuovo dizionario carrello aggiornato per l'utente.

>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
              ('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
{'Banana': 4, 'Apple': 5, 'Orange': 2}

>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
              ['Banana', 'Orange', 'Blueberries', 'Banana'])
{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}

2. Leggere gli articoli elencati nell'app delle note dell'utente

Ops. Sembra che il team di prodotto stia introducendo sempre più funzionalità extra non richieste. Vogliono aggiungere funzionalità extra all'MVP. Ora l'applicazione deve creare un carrello leggendo gli articoli dall'app delle note di un utente. Comodo per gli utenti, ma un po' più di lavoro per il team.

Crea la funzione read_notes(<notes>) che può prendere come argomento un qualsiasi iterabile simile a una lista. La funzione dovrebbe analizzare gli articoli e creare il dizionario carrello dell'utente. Ogni articolo va aggiunto con una quantità di 1. Il nuovo carrello dell'utente va poi restituito.

>>> read_notes(('Banana','Apple', 'Orange'))
{'Banana': 1, 'Apple': 1, 'Orange': 1}

>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}

3. Aggiornare la sezione "idee" delle ricette

L'app ha una sezione "idee" piena di ricette complete di varie cucine. L'utente può selezionare una qualsiasi di queste ricette e farsi aggiungere automaticamente tutti i suoi ingredienti al carrello. Il project manager ti ha chiesto di creare un modo per modificare queste ricette "idee", dato che il team di contenuti continua a cambiare ingredienti e quantità.

Crea la funzione update_recipes(<ideas>, <recipe_updates>) che prende come argomenti un dizionario "idee" e un iterabile di aggiornamenti di ricette. La funzione dovrebbe restituire il dizionario "idee" nuovo o aggiornato.

>>>update_recipes(
    {'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
     'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
    (('Banana Bread', {'Banana': 4,  'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),)
    )
...

{'Banana Bread': {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3}, 
 'Raspberry Pie': {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}

>>> update_recipes(
    {'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
    'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
    'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
    [('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
    ('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
    ('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})]
    )
...

{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1}, 
 'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}, 
 'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
 'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}

4. Ordinare gli articoli nel carrello dell'utente

Una volta che un utente ha iniziato un carrello, l'app gli permette di ordinare gli articoli in ordine alfabetico. Così è più facile trovare le cose, ed aiuta quando ci sono errori di inserimento dei dati, come avere «potatoes» e «Potato» nel database.

Crea la funzione sort_entries(<cart>) che prende come argomento un carrello o dizionario e ne restituisce uno nuovo, ordinato alfabeticamente.

>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}

5. Inviare il carrello dell'utente al negozio per l'evasione

L'app deve inviare il carrello di un determinato utente al negozio per l'evasione. Tuttavia, chi lavora nel negozio deve sapere in quale corsia si trova l'articolo e se l'articolo va tenuto in frigo. Quindi (in modo piuttosto arbitrario) il "carrello di evasione" va ordinato in ordine alfabetico inverso, con le quantità degli articoli combinate con le informazioni sulla posizione e sulla refrigerazione.

Crea la funzione send_to_store(<cart>, <aisle_mapping>) che prende un carrello dell'utente e un dizionario che, per ogni articolo, contiene il numero di corsia del negozio e un valore True/False per indicare se serve la refrigerazione. La funzione dovrebbe return un "carrello di evasione" combinato che contiene (quantità, corsia e refrigerazione) per ogni articolo che il cliente sta ordinando. Gli articoli devono apparire in ordine alfabetico inverso.

>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
                  {'Banana': ['Aisle 5', False], 'Apple': ['Aisle 4', False], 'Orange': ['Aisle 4', False], 'Milk': ['Aisle 2', True]})
{'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]}

6. Aggiornare l'inventario del negozio per riflettere ciò che un utente ha ordinato

L'app non può limitarsi a effettuare ordini dei clienti all'infinito. Prima o poi, al negozio finiranno vari prodotti. Quindi il tuo MVP deve aggiornare l'inventario del negozio ogni volta che un utente invia il proprio ordine al negozio. Altrimenti i clienti ordineranno prodotti che in realtà non sono disponibili.

Crea la funzione update_store_inventory(<fulfillment_cart>, <store_inventory>) che prende un "carrello di evasione" e un inventario del negozio. La funzione dovrebbe ridurre le quantità dell'inventario del negozio del numero "ordinato" nel "carrello di evasione" e poi restituire l'inventario del negozio aggiornato. Quando la quantità di un articolo del negozio scende a 0, la quantità va sostituita con il messaggio 'Out of Stock'.

>>> update_store_inventory({'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]},
{'Banana': [15, 'Aisle 5', False], 'Apple': [12, 'Aisle 4', False], 'Orange': [1, 'Aisle 4', False], 'Milk': [4, 'Aisle 2', True]})

{'Banana': [12, 'Aisle 5', False], 'Apple': [10, 'Aisle 4', False], 'Orange': ['Out of Stock', 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True]}
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Python Exercism

Vuoi iniziare Gestione dei Mecha Munch?

Iscriviti a Exercism per imparare e padroneggiare Python con 17 concetti146 esercizi e il mentoring di persone reali, tutto gratis.