lab

ЛАБОРАТОРНАЯ РАБОТА №5

Тема: Множества. Словари. Генераторы

Цель: Рассмотрение способов работы с множествами, словарями и генераторами

ОГЛАВЛЕНИЕ

1 Множества
1.1 Создание множеств
1.2 Добавление элементов к множеству
1.3 Удаление элементов из множества
1.4 Сравнение и копирование множеств
1.5 Операции над множествами
2 Словари
2.1 Создание словарей
2.2 Методы словарей
3 Генераторы
3.1 Использование генератора для формирования списка
3.2 Использование генератора для формирования множества
3.3 Использование генератора для формирования словаря
4 Копирование объектов
Индивидуальные задания

 Оглавление

1 Множества

Объекты множества в языке Python представляют собой неупорядоченные контейнеры (см. подраздел 1.1 лаб. раб. №3) уникальных хэшируемых объектов.
Объект является хэшируемым, если он имеет значение хэш-функции, которое не меняется в течение времени существования объекта, т.е. поддерживает метод __hash__() и может быть сравним с другими объектами с помощью метода __eq__() (см. подраздел 1.3 лаб. раб. №3). Все хэшируемые объекты являются неизменяемыми объектами и принадлежат ABC-классу Hashable (см. раздел 1 лаб. раб. №3).
Хэшируемость объектов позволяет использовать их в качестве элементов множества и ключей словарей (см. подраздел 2.1), поскольку эти структуры данных используют значения хэш-функции.
В языке Python все встроенные неизменяемые объекты (такие, как числа, логические значения, строки и кортежи) являются хэшируемыми:

>>> from collections import Hashable
>>> isinstance (1.25e-1, Hashable)
True
>>> isinstance ( False , Hashable)
True
>>> isinstance ( 'Web' , Hashable)
True
>>> isinstance ((1,2,3),collections.Hashable)
True

в то время как изменяемые контейнеры, такие как изменяемые множества (см. подраздел 1.1), словари (см. подраздел 2.1) и списки (см. раздел 2 лаб. раб. №3) – нет:

>>> isinstance ([ 'a' , 'b' , 'c' ], Hashable
False

Объекты, которые являются экземплярами созданных пользователем классов, являются хэшироваными по умолчанию, при сравнении между собой они не равны и значения их хэшей являются значениям их id().
Объекты множества могут быть использованы при проверке на наличие элементов в последовательности, при удалении дубликатов из последовательности и при выполнении математических операций над множествами, таких как объединение, пересечение и вычитание.
Имеется два встроенных типа (класса) множеств: set и frozenset (см. подраздел 2.1).
Тип set является изменяемым множеством или просто множеством. Как изменяемый объект, он не может быть использован ни в качестве ключа в словарях, ни в качестве элемента другого множества.
Тип frozenset является неизменяемым множеством. Поэтому может быть ключом в словаре и элементом другого множества.

 Оглавление

1.1 Создание множеств

Создавать экземпляры изменяемого множества (тип set) можно с помощью литерала, поместив в фигурные скобки элементы множества неизменяемых типов, разделенные запятыми:

>>> a_set={1,2, 'a' , 'b' }
>>> a_set
{'b', 1, 2, 'a'},

или с помощью генератора элементов множества (см. подраздел 3.2).
Проверим изменяемость (нехэшируемость) множеств:

>>> isinstance (a_set, Hashable)
False.

Также для создания множества можно использовать конструктор – встроенную функцию set([iterable]) (см. подраздел 8.1 лаб. раб. №1), аргументом которой является итерабельный объект. Поэтому в объекты множества можно преобразовать такие последовательности, как строки, списки, кортежи и диапазоны. Если строка, список, кортеж или диапазон содержат одинаковые элементы, после преобразования из них останется только один:

>>> set ( 'ABBA' )
{'B','A'}.

Отметим, что, поскольку множество является неупорядоченным контейнером, то и элементы полученного множества могут указываться интерпретатором в произвольном порядке.
Раз элементы множества не могут быть изменяемыми типами, то и итерабельный тип, указанный в качестве аргумента функции set() не может содержать изменяемые (нехэшируемые) элементы:

>>> set ([1,2,[3,4]])
TypeError: unhashable type: 'list'

Для создания объектов множества неизменяемого типа frozenset необходимо использовать конструктор – встроенную функцию frozenset() (см. подраздел 8.1 лаб. раб. №1, аргументом которой является итерабельный объект:

>>> frozenset (( 'a' , 'c' , 'd' , 'c' ))
frozenset({'d', 'a', 'c'}) .

Экземпляры типов set и frozenset, являясь контейнерами:

>>> import collections
>>> isinstance ({1,2},collections.Container)
True
>>> isinstance (frozenset({1,2}), collections.Container)
True

и не являясь последовательностями:

>>> isinstance ({1,2},collections.Sequence)
False
>>> isinstance (frozenset({1,2}), collections.Sequence)
False

поддерживают лишь часть операций, свойственных последовательностям (те из них, которые также поддерживают и контейнеры):

Можно добавить, что объекты изменяемого множества set принадлежат ABC-классу MutableSet:

>>> s={1,2,3}
>>> isinstance (s,collections.MutableSet)
True,

а объекты неизменяемого класса frozenset – нет:

>>> fs=frozenset(s)
>>> isinstance (fs,collections.MutableSet)
False

 Оглавление

1.2 Добавление элементов к множеству

Имеется два способа добавления элементов в существующее множество:

Отметим, что объекты типа frozenset, будучи неизменяемыми множествами, не имеют методов добавления элементов к множеству.

 Оглавление

1.3 Удаление элементов из множества

Имеется несколько способов удаления элементов из существующего множества:

В логическом контексте пустое множество set() соответствует False, любое другое – True.
Отметим, что объекты типа frozenset, будучи неизменяемыми множествами, не имеют методов удаления элементов из множества.

 Оглавление

1.4 Сравнение и копирование множеств

Python имеет несколько методов сравнения множеств:

Метод copy() используется для "поверхностного" копирования списков (см. раздел 4)):

>>> b_set=a_set.copy()
>>> b_set
{1, 2, 5, 6}.

 Оглавление

1.5 Операции над множествами

Python имеет несколько методов для выполнения основных операций над множествами:

 Оглавление

2 Словари

Словари в языке Python имеют тип (класс) dict и представляют собой множество пар вида ключ:значение. Словари в языке Python похожи на ассоциативные массивы языка Jaxascript.

 Оглавление

2.1 Создание словарей

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

>>> a_dict={ 'alpha' :1, 'beta' :2, 'gamma' :3}
>>> a_dict
{'alpha': 1, 'beta': 2, 'gamma': 3}

В качестве ключа необходимо использовать только неизменяемые (хэшируемые) типы: числа (целые и с плавающей точкой), строки, логические значения, кортежи, диапазоны и неизменяемые множества. В качестве значения могут быть заданы любые из рассмотренных типов, включая изменяемые множества и словари.
Словари также можно задавать с помощью конструктора – встроенной функции dict() (см. подраздел 8.1 лаб. раб. №1). Если при вызове функция не имеет аргументов, то создается пустой список:

>>> dict ()
{},

если имеет, то для создания словаря могут быть использованы следующие варианты задания аргументов:

Выше четырьмя разными способами были созданы словари a_dict(), b_dict(), c_dict() и d_dict(), имеющие одно и то же значение. Проверим это:

>>> a_dict==b_dict==c_dict==d_dict
True.

Если ключи и значения словаря заданы последовательностями, то для создания словаря также можно использовать встроенную функцию zip(*iterable) (см. подраздел 8.1 лаб. раб. №1), которая возвращает итератор, элементами которого являются кортежи, где i-й кортеж содержит i-е элементы каждого аргумента последовательностей или итерабельных объектов. Если последовательности, явдяющиеся аргументами функции, имеют разную длину, то формируемые кортежи имеют число элементов, равное длине минимальной последовательности. Если указан один элемент – функция возвращает итератор, состоящий из одноэлементных кортежей. Если аргументы не указаны – возвращается пустой итератор.
Пусть, например, ключи словаря представлены списком key_list, а значения – кортежом value_tuple:

>>> key_list=[ 'a' , 'b' , 'c' ]
>>> value_tuple=(1.32, 'value' , [1,2,3], 25)

Теперь с помощью функции zip(), аргументами которой являются последовательности key_list и value_tuple, создаем итератор с именем zipped:

>>> zipped= zip (key_list, value_tuple),

который имеет тип zip:

>>> type (zipped)
<class 'zip'>

Остается только преобразовать его с помощью конструктора в словарь:

>>> e_dict= dict (zipped)
>>> e_dict
{'a': 1.32, 'b': 'value', 'c': [1, 2, 3]}

Следует сказать, что словари в настоящее время являются единственными представителями ABC-класса Mapping, объекты которого ставят в соответсвие неизменяемые значения произвольным объектам. Являются изменяемыми контейнерами типа Mapping:

>>> import collections
>>> isinstance ({1: 'a' ,2: 'b' ,3: 'c' },collections.Container)
True
>>> isinstance ({1: 'a' ,2: 'b' ,3: 'c' },collections.Mapping)
True
>>> isinstance ({1: 'a' ,2: 'b' ,3: 'c' },collections.MutableMapping)
True

Для получения значения словаря по ключу нобходимо воспользоваться выражением:

>>> a_dict[ 'beta' ]
2,

если указанный ключ отсутствует – возбуждается исключение KeyError:

>>> a_dict[ 'delta' ]
KeyError: 'delta'.

Чтобы получить все значения словаря следует использовать оператор for in:

>>> for key in a_dict:
print (key, '=' ,a_dict[key])
alpha = 1
beta = 2
gamma = 3

Можно изменять значение ключа:

>>> a_dict[ 'alpha' ]=11
>>> a_dict
{'alpha': 11, 'beta': 2, 'gamma': 3}

и добавлять новые пары в словарь:

>>> a_dict[ 'epsilon' ]=5
>>> a_dict
{'alpha': 22, 'beta': 2, 'gamma': 3, 'epsilon': 5}

Для удаления ключа необходимо указать:

>>> del a_dict[ 'alpha' ]
>>> ]
{'beta': 2, 'gamma': 3, 'epsilon': 5}.

Словари так же, как последовательности и множества поддерживают функцию определения числа элементов len() и оператор in, позволяющий определить принадлежность элемента контейнеру.
Пустой словарь – {} в логическом контексте равен False, все остальные словари – True.

 Оглавление

2.2 Методы словарей

Словари поддерживают следующие методы:

 Оглавление

3 Генераторы

В литералах при создании списков, множеств и словарей помимо указания перечня их элементов (см. подразделы 2.1 лаб. раб. №3, 1.1 и 2.1) можно также указать генераторы элементов этих контейнеров.

 Оглавление

3.1 Использование генератора для формирования списка

Например, создание списка кубов целых чилел от 1 до 10 можно осуществить следующим образом:

>>> a_list=[x**3 for x in range (1, 11)]
>>> a_list
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000].

Выражение x**3 for x in range(1,11) является генераторным выражением. Оно имеет тип генератора:

>>> type (x**3 for x in range (1, 11))
<class 'generator'>

и состоит из выражения x**3, за которым следует оператор for для итерабельного типа. В таком виде генераторное выражение может использоваться только в качестве единичного аргумента в функциях. В остальных случаях необходимо указывать его в скобках, например при анализе, является ли тип generator итерабельным:

>>> import collections
>>> isinstance ((x**3 for x in range (1, 11)), collections.Iterable)
True.

В приведенном примере сформированные кубы целых чисел сохранены в памяти в виде списка. Для длинных последовательностей это не всегда удобно. Рассмотрим вариант генерации кубов чисел без сохранения их в памяти:

>>> gen=(x**3 for x in range (1, 11))
>>> for x in gen:
print x, end= ' ' )
1, 8, 27, 64, 125, 216, 343, 512, 729, 1000.

Поскольку значения генератора gen не сохранены в памяти повторное его использование ничего не дает:

>>> for x in gen:
print x, end= ' ' )

 Оглавление

3.2 Использование генератора для формирования множества

Создадим с помощью генератора множество букв, из которых состоит предложение "Карл украл у Клары кораллы", представив их в верхнем регистре:

>>> a_set={x for x in 'Карл украл у Клары кораллы' .upper()}
>>> a_set
{'О', 'Л', 'К', 'А', ' ', 'Ы', 'Р', 'У'} .

 Оглавление

3.3 Использование генератора для формирования словаря

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

>>> {x:x**(1/2) for x in [2, 5, 25] }
{25: 5.0, 2: 1.4142135623730951, 5: 2.23606797749979}.

Формировать элементы последовательностей также можно с использованием встроенной функции map() (см. подраздел 1.5.2 лаб. раб. №6).

 Оглавление

4 Копирование объектов

В подразделе 1.4 при описании метода copy() был использован термин поверхностная копия. Поскольку существует еще и глубокая копия, дадим разъяснение этим терминам.
Операторы присваивания в языке Python не копируют объекты, они только сооздают ссылки на объект. Для контейнеров, которые являются изменяемыми или содержат изменяемые элементы, возникает иногда необходимость их копирования – чтобы можно было изменить копию, не изменяя исходные значения.
Модуль copy содержит два метода:

Различие между между поверхностным (англ. shallow) и глубоким (англ. deep) копированием имеет смысл только для составных объектов (объектов, которые содержат другие объекты, например, списки, словари или экземпляры классов):

В качестве примера создадим и сравним поверзностную и глубокую копию словаря:

>>> import copy
>>> a_dict={1:5,2:{3:25}}
>>> a_dict
{1: 5, 2: {3: 25}}
>>> b_dict=copy.copy(a_dict)
>>> b_dict
{1: 5, 2: {3: 25}}
>>> b_dict[2][3]=77
>>> a_dict
{1: 5, 2: {3: 77}}.

Видно, что при использовании поверхностного копирования словаря (функции copy()) изменения в полученной копии привели к изменению оригинала. При использовании глубокого копирования (функции deepcopy()) этого не произойдет:

>>> a_dict={1:5,2:{3:25}}
>>> c_dict=copy.deepcopy(a_dict)
>>> c_dict[2][3]=77
>>> a_dict
{1: 5, 2: {3: 25}}.

Для создания "поверхностной" копии помимо метода copy() модуля copy() можно также использовать:

 Оглавление

Индивидуальные задания

Разработать программу на языке Python, которая выполняет следующее:

Таблица 1 – Перечень индивидуальных заданий

Номер
п/п
МножествоСловарь
СозданиеМетодыОперацииСозданиеМетоды
131,3412,5,8
221,4322,6,9
311,5233,5,1
432,3143,6,8
522,4454,5,9
612,5314,6,1
731,3227,9,5
821,4132,5,8
911,5442,6,9
1032,3353,5,1
1122,4213,6,8
1212,5124,5,9
1331,3434,6,1
1421,4347,9,5
1511,5252,5,8
1632,3112,6,9
1722,4423,5,1
1812,5333,6,8
1931,3244,5,9
2021,4154,6,1

 Оглавление