Словари

Андрей К…
Последнее изменение:
2
0
0

Книга открытых ордеров на эксмо содержит занятную структуру данных:

{"BTC_USD":{"ask_quantity":"226.82420996","ask_amount":"2992323.79093759","ask_top":"8475","bid_quantity":"11720.48826229","bid_amount":"1243102.82950626","bid_top":"8453.490004","ask":[["8475","0.0591663","501.4343925"],["8480","0.00145227","12.3152496"],["8484.99","0.05216288","442.60151517"]],"bid":[["8453.490004","0.037","312.77913014"],["8453.390004","0.405","3423.62295162"],["8453.290002","0.00175728","14.85479745"]]}}

 

Если вы понимаете, о чем это всё – фигурные скобки, двоеточия, квадратные скобки, кавычки и т.п., то эта статья не для вас. Иначе – welcome aboard! К концу статьи такие вещи как книга ордеров будут щелкаться как орешки.

Роботы и рестораны

Знаете вы или нет, но роботы уже плотно ворвались в нашу жизнь. Например, в Сингапуре роботы заменяют официантов – принимают заказы от клиентов и развозят заказы по столикам и увозят грязную посуду.

Давайте пофантазируем и предположим, что владелец ресторана в Кондапоге решил привлечь внимание посетителей к своему заведению, закупил железо и теперь просит вас написать для них программное обеспечение. Почему он выбрал именно вас и почему вы будете писать код на Питоне, оставим за кадром. Поехали.

Итак, вам надо в удобном виде предоставить меню. В меню есть первые блюда, вторые, напитки, десерты, мороженное и т.п. У каждого блюда есть какая-то единица измерения и стоимость, у некоторых есть возможность выбора ингредиентов.


Реклама


Как не надо:

Начинающий программист начнет с обычных переменных, и в них же утонет. Например, он может написать так:

borsch = 150
kvas = 100

подразумевая, что борщ продается по 150, а квас по 100. Но потом он вспомнит, что квас бывает в большом стакане и маленьком, и появится новый виток мысли:

borsch = 150
kvas_little = 70
kvas_big = 100

Потом внезапно окажется, что к борщу можно подать сметану, белый или черный хлеб. Появится новый набор:

borsch = 150
kvas_little = 70
kvas_big = 100
borsch_extra_1 = "Сметана"
borsch_extra_2 = "Белый хлеб"
borsch_extra_3 = "Черный хлеб"

Потом окажется, что надо на экране выводить список того, что есть в меню, и появится новый пассаж:

print("borsch="+borsch, "kvas_little="+kvas_little)

и т.п. В итоге такой робот выехал в зал, клиент посмотрел «меню» и сказал – я буду borsch со сметаной и kvas_big. Что бы передать заказ клиента на кухню, программисту придется писать что-то такое:

if "borsch":
    print("Клиент заказал borsch за " + str(borsch) + " рублей")
if "kvas_little":
    print("Клиент заказал kvas_little за " + str(kvas_little) + " рублей")
if "kvas_big":
    print("Клиент заказал kvas_big за " + str(kvas_big) + " рублей")

 и т.п.

 В итоге, когда владелец ресторана решит убрать из меню квас и добавить лимонад, а также 50 видов пиццы, программист повесится.


Реклама


Как надо.

Для того, чтобы удобно хранить разнородные статичные данные и получать к ним доступ во многих языках разработаны словари (они же хеш-таблицы, они же ассоциативные массивы). Словарь – это, хм, словарь. Например, англо-русский словарь содержит слова на английском (ключи), которым соответствуют значения на русском. Любой словарь, это связка ключа и значения.

В питоне пустой словарь создается и обозначается так: {}

например

menu = {}

Ключи и значения обозначаются через двоеточие, например, если бы мы хотели перенести код выше в словарь, мы могли бы написать так:

menu = {"borsch":150, "kvas":100}

и потом «вытащить» эти значения вот так:

print(menu["borsch"])
print(menu["kvas"])

но это пока что не добавляет изящности, поэтому мы сделаем красивее: мы в словарь вложим отдельные словари. Любой ключ может ссылаться как на значение, так и на другой словарь, массив, экземпляр класса и т.п. Более красивая структура могла бы иметь такой вид:

menu = {
   "borsch": {
        "price":150,
        "name": "Борщ",
        "extra": ["Сметана", "Белый хлеб", "Черный хлеб"]
    },
    "kvas": {
        "name": "Квас",
        "little": {
           "price": 70,
          "amount": 0.3
        }, 
        "big": {
           "price": 100,
          "amount": 0.5
        } 
   }
}

Сделаем print(menu) и посмотрим, что возвращается:

{'borsch': {'price': 150, 'name': 'Борщ', 'extra': ['Сметана', 'Белый хлеб', 'Черный хлеб']}, 'kvas': {'name': 'Квас', 'little': {'price': 70, 'amount': 0.3}, 'big': {'price': 100, 'amount': 0.5}}}

Уже похоже на ответ exmo, правда?

Теперь, когда клиент спросит «А что у вас есть?», робот может выполнить что то такое (что бы получить ключи массива):

for item in menu:
    print(item)
borsch
kvas

Или вот так:

print(menu.keys())

Но зачем клиенту знать внутренние обозначения? Надо писать по русски. Например, так:

for item in menu:
    print(menu[item]['name'])

Что произошло?

Когда мы пишем for item in menu, питон берет словарь, и, до тех пор, пока в словаре есть ключи, получает следующий ключ и присваивает его переменной item (или как вы её назовете). Разберем подробнее:

menu = {
   "borsch": {
        "price":150,
        "name": "Борщ",
        "extra": ["Сметана", "Белый хлеб", "Черный хлеб"]
    },
    "kvas": {
        "name": "Квас",
        "little": {
           "price": 70,
          "amount": 0.3
        }, 
        "big": {
           "price": 100,
          "amount": 0.5
        } 
   }
}

for any_variable_name in menu:
    print("Текущее значение переменной any_variable_name=", any_variable_name)
    print("В словаре под этим ключом хранится вот что: ", menu[any_variable_name])
    print("Возьмем из вложенного словаря значение name -> ", menu[any_variable_name]['name'])
    print('---')

Похожих результатов можно добиться, если использовать метод словаря items(). Этот метод возвращает сразу и ключ, и значение. Заменим код на вот такой, что бы код был проще и «чище»:

menu = {
   "borsch": {
        "price":150,
        "name": "Борщ",
        "extra": ["Сметана", "Белый хлеб", "Черный хлеб"]
    },
    "kvas": {
        "name": "Квас",
        "little": {
           "price": 70,
          "amount": 0.3
        }, 
        "big": {
           "price": 100,
          "amount": 0.5
        } 
   }
}

for key, value in menu.items():
    print(key, value)
    print(value['name'])
    print('---')

Но бывает такое, что мы не знаем, есть ключ в словаре или нет – например, у борща есть цена price, а у кваса нет. Есть несколько способов проверить – каждый подбирается по ситуации.

menu = {
   "borsch": {
        "price":150,
        "name": "Борщ",
        "extra": ["Сметана", "Белый хлеб", "Черный хлеб"]
    },
    "kvas": {
        "name": "Квас",
        "little": {
           "price": 70,
          "amount": 0.3
        }, 
        "big": {
           "price": 100,
          "amount": 0.5
        } 
   }
}

for key, value in menu.items():
    print("Вариант 1")
    print(value['name'])
    if 'price' in value:
        print("Цена", value['price'])
    print('---')

    print("Вариант 2")
    print(value['name'])
    print("Цена", value.get('price', 'Не указано'))
    print('---')

    print("Вариант 3")
    print(value['name'])
    try:
        print("Цена", value['price'])
    except KeyError:
        print("Цена не указана")
    print('---')

    print("Вылетит с ошибкой, если нет цены")
    print("Цена", value['price'])
    print('---')

В первом случае мы проверили, есть ли ключ, и, если есть, вывели значение.

Во втором, воспользовались методом get(), который либо выведет значение указанного ключа, либо выведет указанное нами значение.

В третьем случае, мы выводим цену в любом случае, но если её нет, то перехватываем ошибку, и пишем своё сообщение.

В четвертом случае выводим как есть, и, если ключа нет, вылетает ошибка (вообще-то такие «ошибки» называются исключениями, но об этом в другой раз).


Реклама


Работа с элементами словаря

До этого мы руками завели словарь (или получили готовый), давайте посмотрим, как можно манипулировать содержимым.

menu = {
    "borsch": {
        "price":150,
        "name": "Борщ",
        "extra": ["Сметана", "Белый хлеб", "Черный хлеб"]
    },
    "kvas": {
        "name": "Квас",
        "little": {
           "price": 70,
          "amount": 0.3
        }, 
        "big": {
           "price": 100,
          "amount": 0.5
        } 
   }
}

# Добавим вареники
menu["vareniki"] = {"price":20, 'name': "Вареники"}
print(menu)

# Удалим вареники
del menu['vareniki']
print(menu)

# Заменим квас на строку
menu['kvas'] = "Закончился"
print(menu)

# Добавим 50 сортов пиццы по 300 рублей
for i in range(100):
    menu["pizza_"+str(i)] = {"price":300, 'name': "Пицца " + str(i)}
print(menu)

# Очистим словарь
menu.clear()
print(menu)

Что бы вам еще рассказать?

Узнать количество ключей словаря можно узнать с помощью len()

print(len(menu))

Иногда людям зачем-то хочется еще и сортировать словари в алфавитном или ином порядке. Вообще, это не задача для словарей, т.к. они спроектированы не для этого, а для того, что бы как можно быстрее возвращать значения для указанного ключа. Но если очень хочется, то можно воспользоваться такой конструкцией:

for k in sorted(menu):
    print(k)

Что бы выводить в обратном порядке, добавьте параметр функции:

for k in sorted(menu, reverse=True):
    print(k)

По сути сейчас словарь не отсортировался, а были получены ключи, которые и были отсортированы.  


Реклама


Заключение

Ну, и для закрепления давайте рассмотрим пример из начала статьи - тот же словарь, в нем другие словари, местами массивы.. Ну, вы сами знаете :)

exmo_response = {"BTC_USD":{"ask_quantity":"226.82420996","ask_amount":"2992323.79093759","ask_top":"8475","bid_quantity":"11720.48826229","bid_amount":"1243102.82950626","bid_top":"8453.490004","ask":[["8475","0.0591663","501.4343925"],["8480","0.00145227","12.3152496"],["8484.99","0.05216288","442.60151517"]],"bid":[["8453.490004","0.037","312.77913014"],["8453.390004","0.405","3423.62295162"],["8453.290002","0.00175728","14.85479745"]]}}

for pair, value in exmo_response.items():
    print("Пара",pair)
    print("Лучшие цены", value['ask_top'], value['bid_top'])
    print("Стаканы:")
    print("Продажа", value['ask'])
    print("Покупка", value['bid'])
    for item in value['ask']:
        print("Готовы продать {amount:0.8f} по курсу {rate:0.8f}".format(
            amount=float(item[1]), rate=float(item[0])
        ))

Всем удачи и позитива )

Комментарии: (2)
07.10.2018 17:02
Доброго времени суток!

Только никак не понял, как также можно онлайн с json ответа сделать в список? 
К примеру с https://poloniex.com/public?command=returnTicker
17.10.2018 16:27
Ну это смотря какого вида структуру данных желаете получить на выходе. Зачем из словаря делать список, не совсем понятно (
Пожалуйста, Авторизуйтесь что бы оставить свой комментарий