pyth_07_touple_dict
Заметка 7. Кортеж и словарь
курса Математический практикум по Питону.
Шокуров Антон В.
shokurov.anton.v@yandex.ru
http://машинноезрение.рф
Версия 0.12

Аннотация

Вводится базовы объекты из Питона (Python): кортеж (touple) и словарь (dict). Ключевые слова: touple, index, count, zip, list, dict, pop, popitem, update, setdefault, keys, items, values.

Это предварительная версия! Любые замечания приветсвуются.

Кортеж, touple

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

Создание

In [1]:
a = (0, 4) # Создаем кортеж в явном виде.
a
Out[1]:
(0, 4)
In [2]:
len(a) # Как и у списка -- его длина, т.е. количество элементов в кортеже.
Out[2]:
2

создания кортежа из одного элемента вещь хитрая.

In [3]:
('один элемент') # Это просто строка.
Out[3]:
'один элемент'
In [4]:
# Нужно так:
('один элемент',) # В конце запятая.
Out[4]:
('один элемент',)

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

In [5]:
# Можно задать некую структурированную иформацию.
фио = ('Ломоносов', 'Михаил', 'Васильевич') 
фио # Фамилия, имя, отчество.
Out[5]:
('Ломоносов', 'Михаил', 'Васильевич')
In [6]:
# Можно индексировать. Причем под заданным индексом
фио[1] # будет информация определенного типа.
# В данном случае, имя.
Out[6]:
'Михаил'
In [7]:
# Можно задать и другие данные.
года = (1711, 1765) # Например, период лет: начало и конец.
In [8]:
person = ( фио, года) # Потом объеденить.
In [9]:
# Можно все задать одним больших выражением.
person2 = ( ('Ги́льберт', 'Дави́д', 'Отто'), (1862, 1943) )
In [10]:
# Смысл кортежа в том, что объекты под фиксированным индексом имеют одинаковую интерпритацию.
print( person[0][1], person[0][0], 'родился в', person[1][0], 'г.')
print( person2[0][1], person2[0][0], 'родился в', person2[1][0], 'г.')
Михаил Ломоносов родился в 1711 г.
Дави́д Ги́льберт родился в 1862 г.
In [11]:
# можно писать общий код:
prnt_bio = lambda pers: print( pers[0][1], pers[0][0], 'родился в', pers[1][0], 'г.')
prnt_bio(person) # Вызов для одного,
prnt_bio(person2) # вызов для другого.
Михаил Ломоносов родился в 1711 г.
Дави́д Ги́льберт родился в 1862 г.

Методы схожи со списком

In [12]:
nums = (2, 1, 3, 1, 5, 6)
In [13]:
# Как и в списке подсчитывает количество элементов равных данному.
nums.count(1)
Out[13]:
2
In [14]:
# Аналогично списку. Поиск элемента равному даному.
nums.index(1) # Возвращает индекс первого вхождения.
Out[14]:
1
In [15]:
# Указав во втором аргументе с кого индекса начинать
nums.index(1, 2) # можно найти второе входение.
Out[15]:
3
In [16]:
# Проверяет принадлежность элемента списку.
4 in nums, 3 in nums
Out[16]:
(False, True)

Чего делать нельзя, а что можно

In [17]:
# Кортеж постоянен, т.е. неизменяемый.
фио[1] = 'Максим' # Поэтому, присваивать новое значение нельзя.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-ce03378bb415> in <module>
      1 # Кортеж постоянен, т.е. неизменяемый.
----> 2 фио[1] = 'Максим' # Поэтому, присваивать новое значение нельзя.

TypeError: 'tuple' object does not support item assignment

Как и менять состав нельзя:

In [18]:
# Пытаемся по аналогии со список добавить элемент.
nums.append(8)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-18-bb909b1bba77> in <module>
      1 # Пытаемся по аналогии со список добавить элемент.
----> 2 nums.append(8)

AttributeError: 'tuple' object has no attribute 'append'
In [19]:
del nums[1] # Удалить поле нельзя.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-19-6731ca5f41f9> in <module>
----> 1 del nums[1] # Удалить поле нельзя.

TypeError: 'tuple' object doesn't support item deletion

Добавить новое поле

In [20]:
# Попробуем путем добавления списка.
person + ['деревня Мишанинская'] # К кортежу список добавить нельзя.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-3b398c417e9f> in <module>
      1 # Попробуем путем добавления списка.
----> 2 person + ['деревня Мишанинская'] # К кортежу список добавить нельзя.

TypeError: can only concatenate tuple (not "list") to tuple
In [21]:
# Поробуем добавить "кортеж".
person + ('деревня Мишанинская')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-c7436a4c1569> in <module>
      1 # Поробуем добавить "кортеж".
----> 2 person + ('деревня Мишанинская')

TypeError: can only concatenate tuple (not "str") to tuple
In [22]:
('деревня Мишанинская') # Скобки ничего не значят.
# Получится просто строка.
Out[22]:
'деревня Мишанинская'
In [23]:
# А теперь уж добавлением кортежа.
person + ('деревня Мишанинская',) # Все дело в запятой.
# А так можно. Мы создали новый набор.
Out[23]:
(('Ломоносов', 'Михаил', 'Васильевич'), (1711, 1765), 'деревня Мишанинская')
In [24]:
person # ожидаемо не изменился
Out[24]:
(('Ломоносов', 'Михаил', 'Васильевич'), (1711, 1765))
In [25]:
person_ex = person + ('деревня Мишанинская',) # Сохраним в переменную.
person_ex
Out[25]:
(('Ломоносов', 'Михаил', 'Васильевич'), (1711, 1765), 'деревня Мишанинская')
In [26]:
# Теперь в поле под индексом 2 город рождения.
person_ex[2]
Out[26]:
'деревня Мишанинская'
In [27]:
prnt_bio = lambda pers: print( pers[0][1], pers[0][0], 'родился в', pers[1][0], 'г.', 'в', pers[2])
prnt_bio( person_ex ) # Выведем расширенную информацию.
Михаил Ломоносов родился в 1711 г. в деревня Мишанинская
In [28]:
prnt_bio( person ) # Разумеется не будет работать с кортежом не имеющего данного поля.
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-28-88319524c964> in <module>
----> 1 prnt_bio( person ) # Разумеется не будет работать с кортежом не имеющего данного поля.

<ipython-input-27-7b148abd6a55> in <lambda>(pers)
----> 1 prnt_bio = lambda pers: print( pers[0][1], pers[0][0], 'родился в', pers[1][0], 'г.', 'в', pers[2])
      2 prnt_bio( person_ex ) # Выведем расширенную информацию.

IndexError: tuple index out of range

Применение

In [29]:
# Создадим два списка.
a = [1, 2, 3]
b = ['a', 'b', 'c']
a, b
Out[29]:
([1, 2, 3], ['a', 'b', 'c'])
In [30]:
# Попарно объеденим их соответствующие элементы.
c = zip( a, b ) # Тоже генератор списка.
c
Out[30]:
<zip at 0x7f797a18c288>
In [31]:
# Сшивает два списка.
d = list( c ) # Элементы которого картеж из элементов исходных список.
d
Out[31]:
[(1, 'a'), (2, 'b'), (3, 'c')]
In [32]:
d[1] # Кортеж из элементов a[1] и b[1]
Out[32]:
(2, 'b')
In [33]:
a[1], b[1]
Out[33]:
(2, 'b')
In [34]:
# Можно сшивать больше одного списка.
e = list( zip( [1,2,3], ['a','b','c'],d))
e
Out[34]:
[(1, 'a', (1, 'a')), (2, 'b', (2, 'b')), (3, 'c', (3, 'c'))]
In [35]:
# Тоже будет кортежом.
e[1] # Только больше элементов.
Out[35]:
(2, 'b', (2, 'b'))

Поэлементное произведение списоков

In [36]:
b = [4,5,6]
In [37]:
c = list( zip(a, b) )
c
Out[37]:
[(1, 4), (2, 5), (3, 6)]
In [38]:
# Получаем список произведения двух списков.
list( map( lambda x: x[0] * x[1], c) )
Out[38]:
[4, 10, 18]

Словарь -- ещё одна сущность очень важная для питона

Создание

In [39]:
country = dict() # Созданем словарь.
In [40]:
# Определяем элементы.
country['Россия'] = 'Москва'
country['Франция'] = 'Париж'
country['Италия'] = 'Рим'
In [41]:
# Ключь может иметь разную природу.
country[8] = 10 # Дали соответствие числу.
In [42]:
# Просмотр словаря. Он задан фигурными скобками.
country # Ключ и значение указаны через двоеточее.
# Сами сущности перечислены через запятую.
Out[42]:
{'Россия': 'Москва', 'Франция': 'Париж', 'Италия': 'Рим', 8: 10}
In [43]:
# Можно и задать словарь таким же способом.
eng2rus = { 'cat':'кошка', 'dog' : 'собака' }
eng2rus
Out[43]:
{'cat': 'кошка', 'dog': 'собака'}
In [44]:
empty = {} # можно и пустой словарь задать.
empty
Out[44]:
{}
In [45]:
type( country ), type( eng2rus ), type( empty ) # Выведем тип явно.
Out[45]:
(dict, dict, dict)
In [46]:
empty = dict() # А теперь можно явно и задать.
empty
Out[46]:
{}
In [47]:
aa = id( eng2rus )
eng2rus_copy = eng2rus.copy() # Делаем копию по аналогии со списком.
bb = id( eng2rus_copy )
eng2rus_copy, aa == bb # Словарь скопирован, и сам объект новый.
Out[47]:
({'cat': 'кошка', 'dog': 'собака'}, False)
In [48]:
b = id(eng2rus_copy)
eng2rus_copy.clear() # Очищаем словарь.
a = id(eng2rus_copy)
eng2rus_copy, a == b # Словарь очищен, а объект сохранился.
Out[48]:
({}, True)

"Индексация" или как извлечь значение

In [49]:
# Обобщается понятие индекса. Он тепер нетолько числовой.
country['Франция'] # В квадратных скобках указывется ключ.
Out[49]:
'Париж'
In [50]:
0 in country # Проверка нахождения ключа в словаре.
Out[50]:
False
In [51]:
# Если ключа нет в словаре, то выдается ошибка.
country['Германия']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-51-af7696405b79> in <module>
      1 # Если ключа нет в словаре, то выдается ошибка.
----> 2 country['Германия']

KeyError: 'Германия'

Можно более безопасно извлекать. Так, чтобы не обрабатывать в яном виде отсутствие ключа.

In [52]:
# Можно попытатся извлечь и в случае отсутсвия ключа
cap = country.get('Германия', 'информации нет')
cap # вернуть значение по умолчанию.
Out[52]:
'информации нет'
In [53]:
# Можно вообще не указать и значения по умолчанию.
cap = country.get('Германия')
cap, type(cap) # Специальное значение и тип.
Out[53]:
(None, NoneType)
In [54]:
country # Значение для ключа используемого в get не появилось.
Out[54]:
{'Россия': 'Москва', 'Франция': 'Париж', 'Италия': 'Рим', 8: 10}
In [55]:
# Но значение тем не менее можно задать.
country['Германия'] = 'Берлин'
country # Ранее в словаре его не было. Теперь есть.
Out[55]:
{'Россия': 'Москва',
 'Франция': 'Париж',
 'Италия': 'Рим',
 8: 10,
 'Германия': 'Берлин'}
In [56]:
# Один словарь обновляет исходный.
country.update({'Россия' : 'Питербург', 'США' : 'Вашингтон'})
country # В исходном обновляются значения (Россия) и добавляются новые (США).
Out[56]:
{'Россия': 'Питербург',
 'Франция': 'Париж',
 'Италия': 'Рим',
 8: 10,
 'Германия': 'Берлин',
 'США': 'Вашингтон'}
In [57]:
# В отличие от get, setdefault присваивает новое зачение,
q = country.setdefault('Канада', 'Оттава') # если его нет в словаре.
q # Как и с get, setdefault возвращает значение даже, если его нет в словаре.
Out[57]:
'Оттава'
In [58]:
# Значение (Оттава) для нового ключа (Канада) появилось в словаре.
country
Out[58]:
{'Россия': 'Питербург',
 'Франция': 'Париж',
 'Италия': 'Рим',
 8: 10,
 'Германия': 'Берлин',
 'США': 'Вашингтон',
 'Канада': 'Оттава'}
In [59]:
# Но, если в словаре ключ есть, то setdefault, в отличие от присвоения,
cap = country.setdefault('Россия', 'Москва') # старое значение не обновляет.
cap # А также, возвращает текущее значение ключа.
Out[59]:
'Питербург'
In [60]:
country # Значение не обновилось.
Out[60]:
{'Россия': 'Питербург',
 'Франция': 'Париж',
 'Италия': 'Рим',
 8: 10,
 'Германия': 'Берлин',
 'США': 'Вашингтон',
 'Канада': 'Оттава'}
In [61]:
country['Россия'] = 'Москва' # а так обновим.
country
Out[61]:
{'Россия': 'Москва',
 'Франция': 'Париж',
 'Италия': 'Рим',
 8: 10,
 'Германия': 'Берлин',
 'США': 'Вашингтон',
 'Канада': 'Оттава'}

Обработка

In [62]:
# Можно извлечь конкретный элемент.
country.pop('Франция')
Out[62]:
'Париж'
In [63]:
country # Франции больше в словаре нет.
Out[63]:
{'Россия': 'Москва',
 'Италия': 'Рим',
 8: 10,
 'Германия': 'Берлин',
 'США': 'Вашингтон',
 'Канада': 'Оттава'}
In [64]:
# Извлекаем сразу одну сущность (случайно,
elem = country.popitem() # на порядок рассчитывать не стоит).
elem # Кортеж из ключа и значения.
Out[64]:
('Канада', 'Оттава')
In [65]:
country
Out[65]:
{'Россия': 'Москва',
 'Италия': 'Рим',
 8: 10,
 'Германия': 'Берлин',
 'США': 'Вашингтон'}
In [66]:
f'Кортеж состоящий из ключа, {elem[0]}, и значения, {elem[1]}.'
Out[66]:
'Кортеж состоящий из ключа, Канада, и значения, Оттава.'

Обработка списком

Как обрабатывать элементы словаря. Например, мы хотим найти элемент словаря с определенным свойстом или что-то со всеми ими сделать.

In [67]:
# Преобразование словаря в список.
cc = list( country ) # Останутся только ключи. Значения пропадут.
cc
Out[67]:
['Россия', 'Италия', 8, 'Германия', 'США']
In [68]:
list( map( print, cc) ) # Например вывести название стран.
Россия
Италия
8
Германия
США
Out[68]:
[None, None, None, None, None]
In [69]:
# Последнее эквивалентно явному вызову метода keys.
dd = list( country.keys() )
dd # Можно узнать какие ключи есть.
Out[69]:
['Россия', 'Италия', 8, 'Германия', 'США']
In [70]:
# Но никто не запрещает "вспомнить" значения.
list( map( lambda x:print('столицей', x, 'является', country[x]), cc) )
# Используем словарь для вывода значений.
столицей Россия является Москва
столицей Италия является Рим
столицей 8 является 10
столицей Германия является Берлин
столицей США является Вашингтон
Out[70]:
[None, None, None, None, None]
In [71]:
# А можно и сразу получить доступ к ключю и его значению.
ii = list( country.items() )
ii
Out[71]:
[('Россия', 'Москва'),
 ('Италия', 'Рим'),
 (8, 10),
 ('Германия', 'Берлин'),
 ('США', 'Вашингтон')]
In [72]:
# Тогда можно так.
list( map( lambda x:print('столицей', x[0], 'является', x[1]), ii) )
столицей Россия является Москва
столицей Италия является Рим
столицей 8 является 10
столицей Германия является Берлин
столицей США является Вашингтон
Out[72]:
[None, None, None, None, None]
In [73]:
country.values() # Данный метод наоборот возвращает значения.
Out[73]:
dict_values(['Москва', 'Рим', 10, 'Берлин', 'Вашингтон'])
In [74]:
stud = dict({'Максим' : 'Россия', 'Аннетт' : 'Франция', 'Дима' : 'Россия', 'Луиза' : 'Франция'})
stud # Словарь оторажает имя на страну из которой приехал студент.
Out[74]:
{'Максим': 'Россия', 'Аннетт': 'Франция', 'Дима': 'Россия', 'Луиза': 'Франция'}
In [75]:
stud.values() # Страны из которых приехали студенты.
Out[75]:
dict_values(['Россия', 'Франция', 'Россия', 'Франция'])
In [76]:
countries = list(stud.values())
countries # Список стран.
Out[76]:
['Россия', 'Франция', 'Россия', 'Франция']
In [77]:
# Благодаря славарю получили список стран без повторов.
dict.fromkeys(countries) # Хак..
Out[77]:
{'Россия': None, 'Франция': None}
In [78]:
ii
Out[78]:
[('Россия', 'Москва'),
 ('Италия', 'Рим'),
 (8, 10),
 ('Германия', 'Берлин'),
 ('США', 'Вашингтон')]

Построим словарь в обратную сторону.

In [79]:
rr=dict()
In [80]:
# Попробуем так.
list(map( lambda i: rr[i[1]] = rr[i[0]], ii))
# Но будет ошибка.
  File "<ipython-input-80-04ed97b43377>", line 2
    list(map( lambda i: rr[i[1]] = rr[i[0]], ii))
             ^
SyntaxError: lambda cannot contain assignment

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

если нельзя, но очень хочется, то можно

In [81]:
# Вызовем метод словаря. setdefault позволяет менять значение.
list(map( lambda i: rr.setdefault(i[1],i[0]), ii)) 
# Ошибки не будет.
Out[81]:
['Россия', 'Италия', 8, 'Германия', 'США']
In [82]:
rr
Out[82]:
{'Москва': 'Россия',
 'Рим': 'Италия',
 10: 8,
 'Берлин': 'Германия',
 'Вашингтон': 'США'}

Сложный ключ

Наборы могут выступать в качестве ключа/индекса у словаря.

In [83]:
name = ['Ломоносов', 'Михаил', 'Васильевич']
name
Out[83]:
['Ломоносов', 'Михаил', 'Васильевич']
In [84]:
advisor = ['Вольф', 'Христиан фон', '?']
advisor
Out[84]:
['Вольф', 'Христиан фон', '?']
In [85]:
# Хотим создать словарь в котором по ключу можно узнать научного руководителя.
advisors = dict()
In [86]:
advisors[name] = advisor # Система выдаст ошибку.
# По сути по причине того, что список мы знаем как изменяемый объект.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-86-93a3595a87e6> in <module>
----> 1 advisors[name] = advisor # Система выдаст ошибку.
      2 # По сути по причине того, что список мы знаем как изменяемый объект.

TypeError: unhashable type: 'list'
In [87]:
name = ('Ломоносов', 'Михаил', 'Васильевич')
advisor = ('Вольф', 'Христиан фон', '?')
name, advisor
Out[87]:
(('Ломоносов', 'Михаил', 'Васильевич'), ('Вольф', 'Христиан фон', '?'))
In [88]:
# Теперь разрешено добавить запись в словарь.
advisors[name] = advisor
In [89]:
advisors # Видим что изменение что надо.
Out[89]:
{('Ломоносов', 'Михаил', 'Васильевич'): ('Вольф', 'Христиан фон', '?')}
In [90]:
f'Научным руководителем {name} является {advisors[name]}.'
Out[90]:
"Научным руководителем ('Ломоносов', 'Михаил', 'Васильевич') является ('Вольф', 'Христиан фон', '?')."
In [ ]: