Интересно, что такие инструменты, как генераторы списков и выражения-генераторы, способны по своему удобству приблизиться к запросам в языке SQL, с тем отличием, что они манипулируют объектами в памяти:
>>> [rec[‘name'] for rec in people if rec[‘age'] >= 45] # SQL-подобный
[‘Sue Jones’] # запрос
>>> [(rec[‘age'] ** 2 if rec[‘age'] >= 45 else rec[‘age']) for rec in people]
[42, 2025] >>> G = (rec[‘name'] for rec in people if rec[‘age'] >= 45)
>>> next(G)
‘Sue Jones’
>>> G = ((rec[‘age'] ** 2 if rec[‘age'] >= 45 else rec[‘age']) for rec in people)
>>> G.__next__()
42
А так как словари являются обычными объектами, к этим записям можно также обращаться с использованием привычного синтаксиса:
>>> for person in people:
print(person[‘name'].split()[-1]) # фамилия
person[‘pay'] *= 1.10 # повышение на 10%
Smith
Jones
>>> for person in people: print(person[‘pay'])
33000.0
44000.0
Вложенные структуры
В предыдущих примерах мы могли бы при желании избежать необходимости писать дополнительный программный код, извлекающий фамилии, еще больше структурировав наши записи. Поскольку в языке все объекты составных типов данных могут вкладываться друг в друга сколь угодно глубоко, мы легко можем конструировать чрезвычайно сложные структуры данных, используя простой синтаксис объектов, а Python сам позаботится о создании компонентов, связывании структур в памяти и освобождении памяти позднее. Это одно из значительных преимуществ таких языков сценариев, как Python.
В следующем примере демонстрируется более структурированная запись, содержащая вложенный словарь, список и кортеж внутри другого словаря:
>>> bob2 = {‘name': {‘first': ‘Bob', ‘last': ‘Smith'},
‘age': 42,
‘job': [‘software', ‘writing'],
‘pay': (40000, 50000)}
Эта запись содержит вложенные структуры, поэтому для доступа к более низкому уровню мы просто будем использовать двойные индексы:
>>> bob2[‘name'] # полное имя Боба
{‘last’: ‘Smith’, ‘first’: ‘Bob’}
>>> bob2[‘name'][‘last'] # фамилия Боба
‘Smith’
>>> bob2[‘pay'][1] # верхний предел оклада Боба
50000
Поле name здесь - это еще один словарь, поэтому вместо того чтобы разбивать строку для извлечения фамилии, мы просто используем операцию индексирования. Кроме того, сотрудники могут занимать несколько должностей, а также иметь верхний и нижний предел оклада. Фактически в подобных ситуациях Python превращается в своеобразный язык запросов - мы можем извлекать и изменять вложенные значения с применением обычных операций над объектами:
>>> for job in bob2[‘job']: print(job) # все должности, занимаемые Бобом
software
writing
>> bob2[‘job'][-1] # последняя должность Боба
‘writing’
>>> bob2[‘job'].append(‘janitor') # Боб получает новую должность >>> bob2
{‘job’: [‘software’, ‘writing’, ‘janitor’], ‘pay’: (40000, 50000), ‘age’: 42, ‘name’: {‘last’: ‘Smith’, ‘first’: ‘Bob’}}
В расширении вложенного списка с помощью метода append нет ничего необычного, потому что в действительности он является независимым объектом. Такие вложенные конструкции могут пригодиться в более сложных приложениях. Однако, чтобы не усложнять примеры, мы сохраним прежнюю, плоскую структуру записей.
Словари словарей
И еще один поворот в реализации нашей базы данных с информацией о людях: мы можем расширить область применения словарей, задействовав еще один словарь для представления самой базы данных. То есть мы можем создать словарь словарей - внешний словарь будет играть роль базы данных, а вложенные словари - роль записей. В отличие от простого списка записей, база данных, представленная в виде словаря, позволит нам сохранять и извлекать записи с помощью символических ключей:
>>> bob = dict(name='Bob Smith', age=42, pay=30000, job='dev')
>>> sue = dict(name='Sue Jones', age=45, pay=40000, job='hdw')
>>> bob
{‘pay’: 30000, ‘job’: ‘dev’, ‘age’: 42, ‘name’: ‘Bob Smith’}
>>> db = {}
>>> db[‘bob'] = bob # ссылки на словари в словаре
>>> db[‘sue'] = sue
>>>
>>> db[‘bob'][‘name'] # извлечь имя Боба
‘Bob Smith’
>>> db[‘sue'][‘pay'] = 50000 # изменить оклад Сью
>>> db[‘sue'][‘pay'] # извлечь оклад Сью
50000
Обратите внимание, что такая организация позволяет нам обращаться к записям непосредственно, без необходимости выполнять поиск в цикле - мы получаем непосредственный доступ к записи с информацией о Бобе за счет использования ключа bob. Это действительно словарь словарей, хотя это и не заметно, если не вывести всю базу данных сразу (для подобных целей удобно использовать модуль pprint форматированного вывода):
>>> db
{‘bob’: {‘pay’: 30000, ‘job’: ‘dev’, ‘age’: 42, ‘name’: ‘Bob Smith’}, ‘sue’: {‘pay’: 50000, ‘job’: ‘hdw’, ‘age’: 45, ‘name’: ‘Sue Jones’}}
>>> import pprint >>> pprint.pprint(db)
{‘bob’: {‘age’: 42, ‘job’: ‘dev’, ‘name’: ‘Bob Smith’, ‘pay’: 30000},
‘sue’: {‘age’: 45, ‘job’: ‘hdw’, ‘name’: ‘Sue Jones’, ‘pay’: 50000}}