Автоматизирай Това Начало на книгата

Речници и структуриране на данните

В тази глава се представя типът данни "речник", който предоставя гъвкав начин за достъпване и подреждане на данните. После речниците се комбинират списъците, за да се създаде модел на морски шах.

Типът данни "речник"

Подобно на списък, речникът (dictionary) е изменим сбор от много стойности. Индексите на речниците, за разлика от списъците, могат да бъдат от различни типове данни, не само целочислен. Речниковите индекси се наричат ключове (keys). Към всеки ключ се асоциира стойност, като те общо се наричат двойка ключ-стойност (key-value pair).

В кода типът речник се указва с използване на фигурни скоби {}. Пример ор интерактивната конзола:

>>> myCat = {'размер': 'дебел', 'цвят': 'сив', 'нрав': 'благ'}

Това присвоява речник на променливата myCat. Ключовете на речника са 'размер', 'цвят', 'нрав'. Стойностите към тези ключове са съответно 'дебел', 'сив', 'благ'. Тези стойности могат да се достъпят посредством техните ключове:

>>> myCat['размер']
'дебел'
>>> 'Моята котка има козина с ' + myCat['цвят'] + ' цвят'
'Моята котка има козина с сив цвят'

Речниците могат да използват и целочислени стойности за ключове, точно както списъците използват цели числа за индекси. Тези стойност могат да не започват от 0 и да бъдат с всякаква стойност.

>>> spam = {12345: 'Код за сейфа', 42: 'Отговорът'}

Разлики между речник и списък

Речниците са неподредени, за разлика от списъците. Първият елемент от списък с име spam е spam[0]. В речник няма "първи" елемент. Редът на елементите е от значение при проверяване дали два списъка са еднакви. При речниците няма значение редът, в който са написани отделни двойки ключ-стойност. Пример от интерактивната конзола:

>>> spam = ['котки', 'кучета', 'елени']
>>> bacon = ['кучета', 'елени', 'котки']
>>> spam == bacon
False
>>> eggs = {'име': 'Софи', 'вид': 'котка', 'възраст': '8'}
>>> ham = {'вид': 'котка', 'възраст': '8', 'име': 'Софи'}
>>> eggs == ham
True

Речмиците не са подредени, така че те не могат да се разрязват като списъци.

Опитът за достъпване на ключ, който не съществува в речника, води до съобщение за грешка KeyError. Негов аналог при списък е съобщението за грешка IndexError, което следва достъпването на "невъзможен" индекс. Пример от интерактивната конзола, който показва съобщение за грешка, защото няма ключ 'цвят':

>>> spam = {'име': 'Софи', 'възраст': 7}
>>> spam['цвят']
Traceback (most recent call last):
    File "<stdin>", line 1, in
KeyError: 'цвят'

Речниците не са подредени, но възможността за използване на произволни стойности като ключове позволява удобно структуриране на данните. Нека е необходима програма за записване на рождените дни на приятелите. Удачно ще е използването на речник с имена за ключове и рождените дни като стойности. Следва кодът на програмата, записана като birthdays.py

➊ birthdays = {'Анелия': '1 Април', 'Боби': '12 Декември', 'Влади': '4 Март'}

while True:
    print('Въведи име: (празно за изход)')
    name = input()
    if name == '':
        break

    ➋ if name in birthdays:
        ➌ print('Рождения ден на ' + name + ' е ' + birthdays[name])
    else:
        print('Няма информация за рождения ден на ' + name)
        print('Кога е рождения ден?')
        bday = input()
        ➍ birthdays[name] = bday
        print('Добавен към базата с рождени дни')

Създава се първоначален речник и се запазва в birthdays ➊. За проверка дали дадено име е съществуващ ключ в реяника се използва запазената дума in ➋, точно както при списъците. Ако името е налично в речника, неговата асоциирана стойност се достъпва с квадратни скоби ➌. Ако все още не съществува - може да се добави с използване на квадратни скоби, комбинирани с оператор за присвояване ➍.

При изпълнение на програмата ще има подобен резултат:

Въведи име: (празно за изход)
Анелия
Рождения ден на Анелия е 1 Април
Въведи име: (празно за изход)
Ева
Няма информация за рождения ден на Ева
Кога е рождения ден?
5 Декември
Добавен към базата с рождени дни
Въведи име: (празно за изход)
Ева
Рождения ден на Ева е 5 Декември
Въведи име: (празно за изход)

Тези данни се "забравят" след приключване на програмата. В Глава9 се показва как може данните да се запишат във файлове на твърдия диск.

Методите keys(), values() и items()

Речниците имат три метода, които връщат подобна на списък стойност, съдържаща ключовете, стойностите или двойките ключ-стойност : keys() (ключове), values() (стойности) и items() (елементи). Върнатите стойности не са истински списъци - те не могат да се променят и нямат метод append(). Но тези типове стойност (съответно dict_keys, dict_values и dict_items) могат да се използват в цикъл for. Пример за използването на методите от интерактивната конзола:

>>> spam = {'цвят': 'червен', 'възраст': 42}
>>> for v in spam.values():
...     print(v)

червен
42

Този for цикъл превърта всяка стойност в речника spam. Цикъл for може да превърта и всички ключове или двойки ключ-стойност:

>>> for k in spam.keys():
...     print(k)

цвят
възраст
>>> for i in spam.items():
...     print(i)

('цвят', 'червен')
('възраст', 42)

С помощта на методите keys(), values() и items() цикъл for може да премине през всички ключове, стойности или двойки в речника. Стойностите от тип dict_items, върнати от метода items(), сас всъщност комплект (tuples) от ключа и стойността.

Подобният на списък резултат от тези методи може да се преобразува до истински списък, като се подаде на функцията list(). Пример от интерактивната конзола:

>>> spam = {'цвят': 'червен', 'възраст': 42}
>>> spam.keys()
dict_keys(['цвят', 'възраст'])
>>> list(spam.keys())
['цвят', 'възраст']

Редът list(spam.keys()) подава върнатата от извикването на метода keys() стойност от тип dict_keys на функцията list(), която връща списък ['цвят', 'възраст'].

С цикъл for може да се използва и множествено присвояване на ключа и стойността в отделни променливи. Пример от интерактивната конзола:

>>> spam = {'цвят': 'червен', 'възраст': 42}
>>> for k, v in spam.items():
...     print('Ключ: ' + k + ' Стойност: ' + str(v))

Ключ: цвят Стойност: червен
Ключ: възраст Стойност: 42

Проверка дали в речник съществува даден ключ или стойност

В предната глава се използваха операторите in и not in за проверка дали в списък съществува дадена стойност. Тези оператори могат да се използват за проверяване дали в речник съществува определен ключ или стойност. Пример от интерактивната конзола:

>>> spam = {'име': 'Софи', 'възраст': 7}
>>> 'име' in spam.keys()
True
>>> 'Софи' in spam.values()
True
>>> 'цвят' in spam.keys()
False
>>> 'цвят' not in spam.keys()
True
>>> 'цвят' in spam
False

За отбелязване е, че 'цвят' in spam е на практика съкратена версия на 'цвят' in spam.keys(). Това винаги може да се използва. За проверка дали стойност е (или не е) ключ в речник може просто да се използва in (или not in) със самия речник.

Методът get()

Досадно е всеки път да се проверява дали в речника има ключ, преди да се използва неговата стойност. За радост речниците имат метод get(), който приема два аргумента - ключът, чиято стойност да се получи, както и стойност, която да се използва, ако ключът не съществува.

Пример от интерактивната конзола:

>>> picnicItems = {'ябълки': 5, 'чаши': 2}
>>> 'Ще донеса ' + str(picnicItems.get('чаши', 0)) + ' чаши.'
'Ще донеса 2 чаши.'
>>> 'Ще донеса ' + str(picnicItems.get('яйца', 0)) + ' яйца.'
'Ще донеса 0 яйца.'

В речника picnicItems няма ключ 'eggs', така че методът get() връща подадената стойност по подразбиране 0. Този код би прдизвикал съобщение за грешка, ако не се използва get() :

>>> picnicItems = {'ябълки': 5, 'чаши': 2}
>>> 'Ще донеса ' + str(picnicItems['яйца']) + ' яйца.'
Traceback (most recent call last):     File "<stdin>", line 1, in
KeyError: 'яйца'

Методът setdefault()

Често се налага в речник да се добави стойност за определен ключ, но само ако за този ключ все още няма стойност. Кодът изглежда нещо подобно на:

>>> spam = {'име': 'Кари', 'възраст': 5}
>>> if 'цвят' not in spam:
    spam['цвят'] = 'черен'

Методът setdefault() позволява това да се извърши с един ред код. Първият аргумент, подаден на метода, е ключът, който да се провери. Вторият аргумент е стойността, която да се добави за този ключ, ако ключът не съществува. Методът setdefault() връща старата (или новата) стойност за ключа. Пример от интерактивната конзола:

>>> spam = {'име': 'Кари', 'възраст': 5}
>>> spam.setdefault('цвят', 'черен')
'черен'
>>> spam
{'име': 'Кари', 'възраст': 5, 'цвят': 'черен'}
>>> spam.setdefault('цвят', 'бял')
'черен'
>>> spam
{'име': 'Кари', 'възраст': 5, 'цвят': 'черен'}

При първото извикване на setdefault() речникът в spam се променя на 'име': 'Кари', 'възраст': 5, 'цвят': 'черен'}. Методът връща 'черен', защото тази стойност е добавена за ключа 'цвят'. При последващото извикване spam.setdefault('цвят', 'бял') стойността за този ключ не е променена на 'бял', защото spam вече има ключ с име 'цвят'.

Методът setdefault() е удобен начин за осигуряване на съществуването на ключ. Следва кратка програма, която пресмята броя на всяка буква в низ. Кодът на програмата, записана като characterCount.py:

message = 'Априлският ден бе ясен и студен, часовниците биеха тринайсет часа.'
count = {}

for character in message:
    ➊count.setdefault(character, 0)
    ➋count[character] = count[character] + 1

print(count)

Програмата преминава през всяка буква в низа, записан в променливата message, като брои колко често среща всяка буква. Извикването на метода setdefault() ➊ осигурява съществуването на ключа в речника count (със стойност по подразбиране 0). Така програмата не предизвиква грешка KeyError при изпълнение на count[character] = count[character] + 1 ➋. При изпълнение на програмата се получава подобен резултат:

{'А': 1, 'п': 1, 'р': 2, 'и': 7, 'л': 1, 'с': 6, 'к': 1, 'я': 2, 'т': 5, ' ': 9, 'д': 2, 'е': 7, 'н': 5, 'б': 2, 'у': 1, ',': 1, 'ч': 2, 'а': 5, 'о': 1, 'в': 1, 'ц': 1, 'х': 1, 'й': 1, '.': 1}

От резултата става ясно, че малка буква с се среща 6 пъти, символът за интервал се среща 9 пъти, а главна буква А се среща веднъж. Тази програма работи без значение на низа в променливата message, дори ако низът има милиони букви!

Красиво форматиране - pretty printing

В модула pprint има функции pprint() и pformat(), които могат да "форматират красиво" (pretty print) стойностите на речник. Те позволяват по-изчистено показване на елементите на речника, отколкото с print(). Следва модифицана версия на предната програма characterCount.py, записана като prettyCharacterCount.py.

import pprint
message = 'Априлският ден бе ясен и студен, часовниците биеха тринайсет часа.'
count = {}

for character in message:
    ➊count.setdefault(character, 0)
    ➋count[character] = count[character] + 1

pprint.pprint(count)

При изпълнение на тази програма се получава по-изчистен резултат, със сортирани ключове.

{' ': 9,
',': 1,
'.': 1,
'А': 1,
'а': 5,
-- изрязани редове --
'ц': 1,
'ч': 2,
'я': 2}

Функцията pprint.pprint() е особено полезна, когато речника съдържа стойности, които са списъци или речници.

Функцията pprint.pformat() връща форматирания текст като низ, без да го показва на екрана. Следващите два реда са еквивалентни:

pprint.pprint(someDictionaryValue)
print(pprint.pformat(someDictionaryValue))

Използване на структури от данни за моделиране на реалния свят

Преди да има интернет пак беше възможно да се играе шах с човек, намиращ се на другия край на света. Всеки играч нарежда шахматна дъска и след това праща хода си по пощата до другия играч. За тази цел е необходим начин за описание на състоянието на дъската и направения ход.

Такъв начин за описание е алгебричната шахматна нотация. При нея всеки квадрат от дъската има координати, определени с номер на реда и буква на колоната

Алгебрична шахматна нотация
Координатите на шахматната дъска при използване на алгебрична шахматна нотация

Шахматните фигури се обозначават с букви: Ц за цар, Д за дама, Т за топ, О за офицер и К за кон. Ходът на един играч се описва с буквата на фигурата и координатите на полето, на което отива. Един пълен ход се описва с ходовете на двамата играчи (белите местят първи). Например 2. Кf3 Кc6 означава, че втория ход белите са преместили кон на f3, а черните са преместили кон на c6.

Алгебричната нотация има още детайли, но основното е еднозначното описание на играта, без да има нужда от реална шахматна дъска. Другият играч може да бъде навсякъде по света! Всъщност дори няма нужда от реална дъска. При добра памет може просто да се четат отделните ходове, а дъската да е само въображаема.

Компютрите имат много добра памет. Съвременен компютър може да изпълява програма, която лесно да запази милиарди низове като '2. Kf3 Kc5'. По този начин компютрите могат да изграят шах, без да имат истинкса дъска. Те представят дъската в определен моде, а после се пише код, който работи с този модел.

Тук се намесват списъците и речниците. Позицията отдолу може да се представи с речника {'1h': 'чцар', '6c': ' бдама', '2g': 'чофицер', '5h': 'чдама', '3e': 'бцар'}.

Позиция, моделирана с речника {'1h': 'чцар', '6c': ' бдама', '2g': 'чофицер', '5h': 'чдама', '3e': 'бцар'}
Позиция, моделирана с речника {'1h': 'чцар', '6c': ' бдама', '2g': 'чофицер', '5h': 'чдама', '3e': 'бцар'}

За друг пример ще се използва игра с по-прости правила - морски шах.

Дъска за морски шах

Дъската за морски шах приллича на голяма решетка (#) с девет полета. Всяко поле може да съдържа X, O или да е празно. Речник може да е модел на дъската, като за всяко поле има ключ с определено име, както е показано на следващата картинка.

Полетата на морски шах и техните съответстващи ключове
Полетата на морски шах и техните съответстващи ключове

За представяне на съдържанието на всяко моле могат да се използват низовете 'X', 'O' или ' ' (интервал). Следователно трябва да се "помнят" девет низови стойности. Подходящо е да се използва речник с тези стойности. Стойността с ключ 'Г-Д' представя горния десен ъгъл, стойността с ключ 'Д-Л' представя долния ляв ъгъл, стойността с ключ 'Ср-Ср' представя централното поле и така нататък.

Този речник е структура от данни, представляваща състоянието на дъска за морски шах. Нека има следния речник :

theBoard = {'Г-Л': ' ', 'Г-Ср': ' ', 'Г-Д': ' ',
        'Ср-Л': ' ', 'Ср-Ср': ' ', 'Ср-Д': ' ',
        'Д-Л': ' ', 'Д-Ср': ' ', 'Д-Д': ' '}

Структурата от данни, записана в променливата theBoard представя следната дъска за морски шах:

Празна дъска за морски шах
Празна дъска за морски шах

Стойностите за всички ключове в theBoard са низ с един интервал, така че този речник представя напълно празна дъска. Нека играчът с X е първи и избере средата. Новата дъска може да се представи със следния речник:

theBoard = {'Г-Л': ' ', 'Г-Ср': ' ', 'Г-Д': ' ',
        'Ср-Л': ' ', 'Ср-Ср': 'Х', 'Ср-Д': ' ',
        'Д-Л': ' ', 'Д-Ср': ' ', 'Д-Д': ' '}

Сега структурата от данни в theBoard представя следната позиция на дъската за морски шах:

Първият ход
Първият ход

Позиция, в която играчът с О е спечелил с О на най-горния ред, може да изглежда подобно на:

theBoard = {'Г-Л': 'О', 'Г-Ср': 'О', 'Г-Д': 'О',
        'Ср-Л': 'Х', 'Ср-Ср': 'Х', 'Ср-Д': ' ',
        'Д-Л': ' ', 'Д-Ср': ' ', 'Д-Д': 'Х'}

Сега структурата от данни в theBoard представя следната позиция на дъската за морски шах:

Играчът О печели.
Играчът О печели.

Разбира се, играчите виждат само каквото е показано на екрана, не съдържанието на променливи. Трябва да се създаде функция, която показва на екрана дъската спрямо речника. Следва допълнение към програмата morskiShah.py (новият код е удебелен):

theBoard = {'Г-Л': ' ', 'Г-Ср': ' ', 'Г-Д': ' ',
        'Ср-Л': ' ', 'Ср-Ср': ' ', 'Ср-Д': ' ',
        'Д-Л': ' ', 'Д-Ср': ' ', 'Д-Д': ' '}

def printBoard(board):
    print(board['Г-Л'] + '|' + board['Г-Ср'] + '|' + board['Г-Д'])
    print('-+-+-')
    print(board['Ср-Л'] + '|' + board['Ср-Ср'] + '|' + board['Ср-Д'])
    print('-+-+-')
    print(board['Д-Л'] + '|' + board['Д-Ср'] + '|' + board['Д-Д'])

printBoard(theBoard)

При изпълнение на програмата printBoard() ще покаже празна дъска за морски шах.

 | |
-+-+-
 | |
-+-+-
 | |

Функцията printBoard() може да обработи всяка структура от данни за морски шах, която ѝ се подаде. Кодът може да се промени на:

theBoard = {'Г-Л': 'О', 'Г-Ср': 'О', 'Г-Д': 'О',
        'Ср-Л': 'Х', 'Ср-Ср': 'Х', 'Ср-Д': ' ',
        'Д-Л': ' ', 'Д-Ср': ' ', 'Д-Д': 'Х'}

def printBoard(board):
        print(board['Г-Л'] + '|' + board['Г-Ср'] + '|' + board['Г-Д'])
        print('-+-+-')
        print(board['Ср-Л'] + '|' + board['Ср-Ср'] + '|' + board['Ср-Д'])
        print('-+-+-')
        print(board['Д-Л'] + '|' + board['Д-Ср'] + '|' + board['Д-Д'])

printBoard(theBoard)

При изпълнение на програмата на екрана се показва друга дъска.

О|О|О
-+-+-
Х|Х|
-+-+-
 | |Х

Вече има структура, която представя позиция на морски шах, както и printBoard() за визуализация на тази структура от данни. На практика има програма, която "моделира" морски шах. Структурата от данни може да е организирана по различен начин (например ключовете да са от рода на 'ГОРЕ-ДЯСНО', вместо 'Г-Д'), но програмата ще е коретка, докато работи с тази структура.

Един пример за това е функцията printBoard(), която очаква структурата от данни да е речник с ключове и стойности за всички девет полета. Ако в подаденият речник липсва някой ключ, примерно 'Ср-Л', програмата няма да работи.

О|О|О
-+-+-

Traceback (most recent call last):
     File "morskiShah.py", line 16, in <module>
        printBoard(theBoard)
File "morskiShah.py", line 12, in printBoard
        print(board['Ср-Л'] + '|' + board['Ср-Ср'] + '|' + board['Ср-Д'])
KeyError: 'Ср-Л'

Следва код, който позволява на играчите да въвъждат техните ходове. Променената програма morskiShah.py изглежда така:

theBoard = {'Г-Л': ' ', 'Г-Ср': ' ', 'Г-Д': ' ',
        'Ср-Л': ' ', 'Ср-Ср': ' ', 'Ср-Д': ' ',
        'Д-Л': ' ', 'Д-Ср': ' ', 'Д-Д': ' '}

def printBoard(board):
        print(board['Г-Л'] + '|' + board['Г-Ср'] + '|' + board['Г-Д'])
        print('-+-+-')
        print(board['Ср-Л'] + '|' + board['Ср-Ср'] + '|' + board['Ср-Д'])
        print('-+-+-')
        print(board['Д-Л'] + '|' + board['Д-Ср'] + '|' + board['Д-Д'])

turn = 'Х'
for i in range(9):
    ➊ printBoard(theBoard)
    print('На ход е ' + turn + '. На кое поле?')
    ➋ move = input()
    ➌ theBoard[move] = turn
    ➍ if turn == 'Х':
        turn = 'О'
    else:
        turn = 'Х'

printBoard(theBoard)

Новият код показва текущото състояние в преди всеки ход ➊, получава хода на играча ➋, осъвременява позицията на дъската ➌ и дава хода на другия играч ➍. След това се преминава към следващия ход.

При изпълнение на програмата се получава подобен резултат:

| |
-+-+-
| |
-+-+-
| |
На ход е Х. На кое поле?
Ср-Ср
| |
-+-+-
|Х|
-+-+-
| |

-- изрязани редове --
О|О|Х
-+-+-
Х|Х|О
-+-+-
О| |Х
На ход е Х. На кое поле?
Д-Ср
О|О|Х
-+-+-
Х|Х|О
-+-+-
О|Х|Х

Това не е завършена игра на морски шах. Тя неикога не проверява дали някой играч е спечелил. Все пак тя е достатъчна, за демонстрация на структурите от данни, които могат да се използват.

Вложени речници и списъци

Моделирането на морски шах е сравнително просто - има нужда от само един речник с девет двойки ключ-стойност. При моделиране на по-сложни неща може да потрябват речници и списъци, които да съдържат други речници и списъци. Списъците са полезни за наредени поредици от стойности, а речниците са полезни за свързване на ключове със стойности. Следващата примерна програма използва речник, който съдържа други речници, описващи какво ще носят гостите на пикник. Функцията totalBrought() прочита тази структура и пресмята общия брой за всяко нещо, донесено от гостите.

allGuests = {'Анелия': {'ябълки': 5, 'гевреци': 12},
            'Боби': {'сандвичи': 3, 'ябълки': 2},
            'Владо': {'чаши': 3, 'ябълков кекс': 1} }

def totalBrought(guests, item):
    numBrought = 0
    ➊ for k, v in guests.items():
        ➋ numBrought = numBrought + v.get(item, 0)
    return numBrought

print('Брой на донесени неща:')
print('- Ябълки : ' + str(totalBrought(allGuests, 'ябълки')))
print('- Чаши : ' + str(totalBrought(allGuests, 'чаши')))
print('- Торти : ' + str(totalBrought(allGuests, 'торти')))
print('- Сандвичи : ' + str(totalBrought(allGuests, 'сандвичи')))
print('- Ябълков кекс : ' + str(totalBrought(allGuests, 'ябълков кекс')))

В тялото на функцията totalBrought() има цикъл for, който минава през двойките ключ-стойност в allGuests ➊. В този цикъл на променливата k се присвоява низ с името на госта, а на променливата v се присвоява речникът с неща, които гостът носи. Ако параметърът item присъства като ключ във вложения речник - неговата стойност (количеството) се добавя към numBrought ➋. Ако не присъства - методът get() връща 0, която да се добави към numBrought.

Резултатът от изпълнението на програмата изглежда така:

Брой на донесени неща:
- Ябълки : 7
- Чаши : 3
- Торти : 0
- Сандвичи : 3
- Ябълков кекс : 1

Това може да изглежда като толкова лесен проблем за моделиране, че не е необходимо да се губи време за писане на програма. Но трябва да се осъзнае, че без никаква промяна функцията totalBrought() може лесно да обработи речник, който съдържа хиляди гости, всеки от които носи хиляди различни неща за пикник. В такъв случай функцията totalBrought(), заедно с информацията в структура от данни, може да спести много време!

Моделирането на света с помощта на структури от данни може да става по различни начини. Важното е останалата част от кода в програмата да може да работи правилно с избрания модел на данните. В началото човек не трябва да се тревожи особено за "правилния" начин за моделиране на данните. С придобиването на опит се намират по-ефективни модели, но основното е моделът на данните да е удачен за нуждите на програмата.

Обобщение

Тази глава описа напълно речниците. Списъците и речниците са стойности, които могат да съдържат множество от стойности, включително дрги списъци и речници. Речниците са полезни за свързване на един елемент (ключът) с друг (стойността), за разлика от списъците, които просто съдържат подредени стойности. Стойностите в речниците се достъпват с квадратни скоби, точно както и списъците. Вместо числен индекс речниците могат да имат ключове от различни типове : целочислени, дробни, низове или комплекти. Обектите от реалния свят могат да се представят в програмата чрез организиране на стойностите в структури от данни. Добър пример за моделиране беше представен с дъската за морски шах.

Упражнения - въпроси

  1. Как изглежда код, който създава празен речник?
  2. Как изглежда речник, с ключ 'нещо' и стойност 42?
  3. Коя е основната разлика между речник и списък?
  4. Какъв ще е резултата от кода spam['нещо'], ако spam е {'друго': 100}?
  5. Нека променливата spam съдържа речник. Каква е разликата между израза 'котка' in spam и 'котка' in spam.keys()?
  6. Нека променливата spam съдържа речник. Каква е разликата между израза 'котка' in spam и 'котка' in spam.values()?
  7. Как по-кратко може да се напише следния код?

    if 'цвят' not in spam:
        spam['цвят'] = 'черен'

  8. Кой модул и функция може да се използва за "красиво форматиране" на речник?

Практически упражнения

Да се напишат програми, които да вършат следните задачи.

Валидиране на шахматна позиция

В тази глава за описване на позиция на шахматна дъска се използва речника {'1h': 'чцар', '6c': ' бдама', '2g': 'чофицер', '5h': 'чдама', '3e': 'бцар'}. Да се напише функция с име isValidChessBoard(), която получава речник като аргумент и връща True или False в зависимост дали позицията е позволена.

Позволена позиция ще има точно един черен цар и точно един бял цар. Всеки играч може да има най-много 16 фигури, най-много 8 пешки. Също така всички фигури трябва да са на позволен квадрат, от '1a' до '8h'(тоест не може фигура да бъде на '9z'). Името на фигурата трябва да започва с 'б' или 'ч', съответно за бял и черен цвят, последвано от 'пешка', 'кон', 'офицер', 'топ', 'дама' или 'цар'. Тази функция може да се използва за проверка дали няма проблем в програмата, който води до достигане на неправилни шах позиции.

Инвентар в измислена игра

Създава се измислена видео игра. Структурата от данни, моделираща инвентара на играча, ще е речник. Ключовете са низове, описващи предмета в инвентара, а стойността ще е цяло число, указващо колко от този предмет има играча. Примерен речник е {'въже': 1, 'факел': 6, 'златна монета': 42, 'кинжал': 1, 'срела': 12}, който означава, че играча има 1 въже, 6 факела, 42 златни монети и така нататък.

Да се напише функция dispalyInventory(), която приема какъвто и да е възможен "инвентар" и го изобразява по следния начин :

Инвентар:
12 стрели
42 златна монета
1 въже
6 факел
1 кинжал
Общ брой предмети : 62

Подсказка: Може да се използва цикъл for за минаване през всички ключове в речника.

stuff = {'въже': 1, 'факла': 6, 'златна монета': 42, 'кинжал': 1, 'срела': 12}

def displayInventory(inventory):
    print('Инвентар:')
    item_total = 0
    for k, v in inventory.items():
        #КОДЪТ ТРЯБВА ДА Е ТУК
    print('Общ брой предмети : ' + str(item_total))

displayInventory(stuff)

Инвентар в измислена игра : Преобразувне на списък в речник

Играта продължава. Нека съдържанието на съндъка на победен дракон е представен като списък от низове, подобен на:

dragonLoot = ['златна монета', 'кинжал', 'златна монета', 'златна монета', 'рубин']

Да се напише функция с име addToInventory(inventory, addedItems). Аргументът inventory е речник, който представя инвентара на играча (както в предишната задача). Параметърът addedItems е подобен на dragonLoot. Тази функция трябва да върне речник, който да представя променения инвентар (с добавените предмети от списъка). Трябва да се отбележи, че списъка addedItems може да съдържа един елемент по няколко пъти. Кодът може да е подобен на:

def addToInventory(inventory, addedItems):
#Място за кода

inv = {'златна монета': 42, 'въже': 1}
dragonLoot = ['златна монета', 'кинжал', 'златна монета', 'златна монета', 'рубин']
inv = addToInventory(inv, dragonLoot)
displayInventory(inv)

Последната програма (която използва функцията displayInventory() от предишната задача) ще има следния резултат:

Инвентар:
златна монета 45
въже 1
кинжал 1
рубин 1
Общ брой предмети : 48

Подкрепете автора чрез покупка на оригиналната книга от No Starch Press или Amazon.