Клас dict у Python надає багато корисних методів для роботи зі словниками. Деякі з них було представлено в концепції dicts. Тут ми розглянемо ще кілька з них, а також деякі прийоми для перебирання словників та роботи з ними.
setdefault() для вставлення без помилокКонцепція словників раніше розповідала, що .get(key, <default value>) повертає наявне value або default value, якщо key не знайдено у словнику, і таким чином дозволяє уникнути KeyError. Це добре працює в ситуаціях, коли ми воліли б не мати додаткового опрацювання помилок, але не можемо бути впевнені, що шуканий key буде наявний.
Для подібної «безпечної» (без KeyError) операції вставлення існує метод .setdefault(key, <default value>). setdefault(key, <default value>) поверне value, якщо key знайдено у словнику. Якщо ключ не знайдено, він вставить пару (key, default value) і поверне default value для використання.
>>> 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'}
fromkeys() для наповнення словника з ітерабельного обʼєктаЩоб швидко наповнити словник різними keys і типовими значеннями, метод класу fromkeys(iterable, <default value>) перебере ітерабельний обʼєкт з keys і створить новий dict. Усі values буде встановлено в надане default value:
>>> 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'}
Методи .keys(), .values() і .items() повертають ітерабельні подання словника.
Ці подання можна використовувати, щоб легко перебирати записи, не змінюючи їх. Подання також динамічні: коли дані словника, що лежить в основі, змінюються, повʼязаний view object відобразить цю зміну:
>>> 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')])
.keys(), .values() і .items()
У Python 3.7+ dicts зберігають порядок, у якому вставляються записи, що дозволяє ітерацію за принципом «перший увійшов, перший вийшов» (FIFO), коли ми використовуємо .keys(), .values() або .items().
У Python 3.8+ подання також оборотні. Це дозволяє перебирати ключі, значення або пари (key, value) у порядку «останній увійшов, перший вийшов» (LIFO), використовуючи reversed(<dict>.keys()), reversed(<dict>.values()) або 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')
Словники не мають вбудованого методу сортування. Однак можна відсортувати подання dict за допомогою вбудованої функції sorted() разом із dict.items(). Відсортоване подання потім можна використати, щоб створити новий словник. Як і під час ітерації, типове сортування виконується за keys словника.
# 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'}
.update()
<dict_one>.update(<dict_two>) можна використати, щоб поєднати два словники. Цей метод візьме пари (key,value) з <dict_two> і запише їх у <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'}
Там, де ключі у двох словниках збігаються, value у dict_one буде перезаписано відповідним value з 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'}
| і |=)Python 3.9 представляє інший спосіб обʼєднання dicts: оператори union. dict_one | dict_two створить новий словник, що складається з пар (key, value) з dict_one і dict_two. Коли обидва словники мають спільні ключі, значення з dict_two мають пріоритет.
>>> 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 поводиться подібно до <dict_one>.update(<other>), але в цьому випадку other може бути або dict, або ітерабельним обʼєктом з пар (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'}
Докладне пояснення словників і методів роботи з ними можна знайти в офіційному підручнику та офіційному довіднику бібліотеки: це чудові відправні точки.
Real Python і Finxter також мають дуже ґрунтовні статті про словники Python.
Mecha Munch™, компанія з автоматизації покупок продуктів, щойно найняла нас працювати над їхнім застосунком для замовлень. Перед нашою командою стоїть завдання створити MVP (мінімально життєздатний продукт), який керує всіма базовими діями з кошиком для покупок і дає користувачам змогу додавати, видаляти та сортувати свої замовлення. На щастя, усі фінансові питання та оформлення замовлень бере на себе інша команда!
MVP має давати користувачеві змогу додавати товари до свого кошика для покупок. Це може бути один товар або кілька товарів одразу. Оскільки йдеться про MVP, кількість товару позначається повторами. Якщо користувач хоче додати 2 апельсини, «Oranges» зʼявиться двічі у вхідному обʼєкті, який можна перебрати. Якщо цей товар уже є в кошику користувача, його кількість у кошику потрібно збільшити на 1. Якщо товар новий для кошика, його додають із кількістю 1.
Створіть функцію add_item(<current_cart>, <items_to_add>), яка як аргументи приймає словник кошика та будь-який схожий на список обʼєкт із товарами для додавання, який можна перебрати.
Вона має повернути новий (оновлений) словник кошика користувача.
>>> 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}
Ой-ой. Схоже, продуктова команда взялася за розростання можливостей. Вони хочуть додати до MVP додаткові можливості. Тепер застосунок має створювати кошик для покупок, зчитуючи товари із застосунку нотаток користувача. Зручно для користувачів, але трохи більше роботи для команди.
Створіть функцію read_notes(<notes>), яка приймає як аргумент будь-який схожий на список обʼєкт, який можна перебрати.
Функція має розібрати товари та створити словник кошика користувача.
Кожен товар додається з кількістю 1.
Потім потрібно повернути новий кошик користувача.
>>> 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}
У застосунку є розділ «ideas», наповнений готовими рецептами різних кухонь. Користувач може вибрати будь-який із цих рецептів, і всі його інгредієнти автоматично додадуться до кошика для покупок. Менеджер проєкту попросив нас зробити спосіб редагувати ці рецепти з «ideas», бо команда контенту постійно змінює інгредієнти та кількості.
Створіть функцію update_recipes(<ideas>, <recipe_updates>), яка як аргументи приймає словник «ideas» та обʼєкт з оновленнями рецептів, який можна перебрати.
Функція має повернути новий (оновлений) словник «ideas».
>>>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}}
Коли користувач уже почав кошик, застосунок дає змогу відсортувати товари за абеткою. Так легше щось знайти, і це допомагає, коли трапляються помилки введення даних, як-от «potatoes» і «Potato» у базі даних.
Створіть функцію sort_entries(<cart>), яка приймає кошик (словник) як аргумент і повертає новий, відсортований за абеткою.
>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}
Застосунок має надіслати кошик користувача до магазину для виконання замовлення. Однак працівникам магазину, які збирають замовлення, потрібно знати, у якому відділі магазину лежить товар і чи потребує він охолодження. Тож (досить умовно) «кошик виконання» потрібно відсортувати у зворотному алфавітному порядку, а кількість кожного товару поєднати з інформацією про місце й охолодження.
Створіть функцію send_to_store(<cart>, <aisle_mapping>), яка приймає кошик користувача та словник, де для кожного товару вказано номер відділу магазину й True/False щодо потреби в охолодженні.
Функція має return обʼєднаний «кошик виконання», який містить (кількість, відділ і охолодження) для кожного товару, який замовляє клієнт.
Товари мають бути в зворотному алфавітному порядку.
>>> 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]}
Застосунок не може просто без кінця приймати замовлення клієнтів. Рано чи пізно в магазині закінчаться різні товари. Тож MVP застосунку має оновлювати запаси магазину щоразу, коли користувач надсилає своє замовлення до магазину. Інакше клієнти будуть замовляти товари, яких насправді немає.
Створіть функцію update_store_inventory(<fulfillment_cart>, <store_inventory>), яка приймає «кошик виконання» та запаси магазину.
Функція має зменшити запаси магазину на кількість, «замовлену» в «кошику виконання», а потім повернути оновлені запаси магазину.
Там, де кількість товару в магазині падає до 0, її потрібно замінити повідомленням «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]}
Зареєструйтеся на Exercism, щоб вивчати й опановувати Python, а також 17 концепцій146 вправ та справжнє наставництво від людей, і все це безкоштовно.