Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# Python
## Типы данных-коллекции
### Раздел 1. Типы данных-последовательности
Тип данных, реализующий последовательность, это такой тип данных, для которого доступен оператор вхождения, функция получения длины (len()), получения среза и к нему можно доступаться как к итератору. Мы уже рассмотрели тип str, в будущем будут рассмотрены bytearray и bites, а на данный момент сосредоточимся на tuple и лист. Все эти типы являются встроенными.
#### Кортежи
Кортежем (tuple) называется упорядоченная последовательность из 0 или более ссылок на объекты. Кортежи поддерживают тот же оператор извлечения среза, что и строки. Кортежи являются неизменяемыми, так что если в ходе работы вам необходима изменяемая последовательность — обратитесь к списком, либо преобразуйте кортеж в список при помощи вызова list().
Как и всегда, tuple может быть вызван как функция. Без передачи аргумента, будет возвращен пустой объект. При передачи в качестве аргумента объекта типа tuple, будет создана его поверхностная копия. Во всех остальных случаях будет совершена попытка привести аргумент к типу tuple. Данная функция принимает не более одного аргумента.
Кортежи могут быть созданы заключением элементов в скобки (даже если элементов нет () — пустой кортеж) или перечислением элементов кортежа через запятую (только не при передачи таким образом кортежа в качестве аргумента функции.
Для кортежей доступны всего два метода: count и index. t.count(x) возвращает число вхождений x в t, а t.index(x) возвращает индекс самого левого вхождения x в t, либо возбуждает ValueError, если x не входит в t. К тому же, для кортежей доступны операторы + (конкатенация), \* (повторение), [] (взятие среза), in и not in (проверка на вхождение). Комбинированные версии присваивания += и \*= могут использоваться, несмотря на то, что кортежи неизменяемы. Просто будет создан новый объект, который будет присвоен переменной (то же происходит и со строками). Для кортежей доступны операции сравнения: < <= == != >= >. Они выполняют сравнение поэлементно и рекурсивно.
Кортежи позволяют выполнить один из самых известных трюков в Python:
```python
x = 1
y = 2
x, y = y, x
print(x, y)
```
2 1
Также, если элементами итератора являются кортежи, то вы их сразу можете распаковывать:
```python
for hero, villain in (('Batman', 'Joker'), ('Superman', 'Lex Luthor'), ('Green Lantern', 'Sinestro')):
print(hero, 'vs', villain)
```
Batman vs Joker
Superman vs Lex Luthor
Green Lantern vs Sinestro
#### Списки
Список есть упорядоченная последовательность из нуля или более ссылок на объекты. Как и кортежи, для списков доступно взятие среза. Но в отличие от кортежей, списки изменяемы. Как следствие, мы можем изменять не только список целиком или его отдельные элементы, но и целые срезы в нем.
Тип list может быть вызван как функция. Без аргументов list() вернет пустой список. Если в качестве аргумента был передан объект типа list, то будет создана его поверхностная копия. Если аргумент дан, но не является объектом класса list, то будет произведена попытка приведения его к типу list. Данная функция принимает не более одного аргумента.
Списки могут быть созданы также перечислением элементов через запятую в квадратных скобках или при помощи генераторов списков.
Для типа list доступны операции сравнения, работают аналогично случаю с кортежами.
Списки могут быть вложены в друг друга, проитерированы, от них можно взять срез, — все то же, что и для кортежей. Можно использовать операторы in, not int, +, +=, *, *=, функцию len() и конструкцию del.
Также есть оператор \*, который может использоваться для получения остальной части списка в случае выбора нескольких элементов:
```python
heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash']
batman, *rest = heroes
print(batman, rest)
batman, *mid, flash = heroes
print(batman, mid, flash)
```
Batman ['Superman', 'Green Lantern', 'Flash']
Batman ['Superman', 'Green Lantern'] Flash
Также есть оператор распаковки \*, который используется для передачи элементов списка по отдельности в качестве аргументов функции:
```python
heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash']
def print4(a, b, c, d):
print(a)
print(b)
print(c)
print(d)
print4(*heroes)
```
Batman
Superman
Green Lantern
Flash
Для списков доступны следующие методы:
| Метод | Описание |
| ----- | :------- |
| L.append(x) | Добавляет элемент x в конец списка L |
| L.count(x) | Подсчитывает число вхождений элемента x в список L |
| L.extend(m) | Добавляет элементы m, к которому должен быть обеспечен доступ как к итератору, в конец списка L (то же, что и L += m |
| L.index(x, start, end) | Возвращает индекс самого левого вхождения x в L. Если start и end заданы, то поиск происходит в срезе L[start:end]. Если элемент не найден, то возбуждается исключение ValueError |
| L.insert(i, x) | Вставляет элемент x на позицию с индексом i в список L |
| L.pop() | Удаляет последний элемент списка L и возвращает его |
| L.pop(i) | Удаляет элемент списка L с индексом i и возвращает его |
| L.remove(x) | Удаляет самое левое вхождение элемента x из списка L. Если элемент не найден, то возбуждается исключение ValueError |
| L.reverse() | Разворачивает список |
| L.sort(...) | Сортирует список. Аргумент key отвечает за функцию порядка, а reversed за способ сортировки (по невозрастанию / неубыванию) |
#### Генераторы списков
Для получения списка как результат какой-то операции над итератором используются генераторы списков. Есть две формымы генератора списков:
[выражение for элемент in итератор]
[выражение for элемент in итератор if условие]
В самом простом случае выражение и есть элемент:
```python
print([x for x in range(10)])
print([2*x for x in range(10)])
print([2*x for x in range(10) if x % 3 == 0])
```
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[0, 6, 12, 18]
Последняя форма в разы удобнее, чем запись:
```python
lst = []
for x in range(10):
if x % 3 == 0:
lst.append(2*x)
print(lst)
```
[0, 6, 12, 18]
### Раздел 2. Типы-множества
Типы данных-множества — типы, для которых доступны оператор вхождения in, функция len и доступ, как к итератору. В Python есть два типа множеств — set и frozenset. Тип frozenset является неизменяемым, поэтому для него недоступны методы для типа set, которые изменяют объект. В остальном они идентичны.
Только хешируемые объекты могут быть добавлены в множество. Все встроенные неизменяемые типы являются хешируемыми.
#### Множества
Множество типа set есть неупорядоченная коллекция из нуля или более ссылок на хешируемые объекты. Множества изменяемы, так что в них можно добавлять и удалять элементы. Будучи неупорядоченными, понятие индекса или позиции для них не имеет смысла.
Для создания множества используется либо вызов set(), который без аргументов создает пустое множество. С аргументом типа set создается поверхностная копия множества, а с другим аргументом выполняется попытка приведения к множеству. Данная функция не принимает более одного аргумента. Множество может быть также создано путем заключения элементов в фигурные скобки, но стоит учитывать, что пустое множество не может быть создано таким образом, так как {} создаст пустой словарь.
Множество всегда хранит уникальные элементы:
```python
set('hello world') == {'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'}
```
True
Множества поддерживают функцию len(), которое возвращает число элементов множества.
Для типа set доступны следующие методы:
| Метод | Описание |
| ----- | :------- |
| s.add(x) | Добавляет элемент x во множество s, если такого там нет |
| s.clear() | Удаляет все элементы множества s |
| s.copy() | Создает неглубокую копию множества s |
| s.difference(t) | Возвращает новое множество, которое содержит все элементы множества s, которые не входят во множество t. Эквивалент s - t |
| s.differenct_update(t) | Удаляет все элементы t из множества s. Эквивалент s -= t |
| s.discard(x) | Удаляет элемент x из множества s |
| s.intersection(t) | Возвращает новое множество, которое содержит элементы множества s, которые в то же время являются элементами множества t. Эквивалент s & t |
| s.intersection_update(t) | Удаляет из множества s все элементы, которые не входят в t. Эквивалент s &= t |
| s.isdisjoint(t) | Возвращает True, если у множеств s и t нет общих элементов |
| s.issubset(t) | Возвращает True, если s есть подмножество (несобственное) t. Эквивалент s <= t |
| s.issuperset(t) | Возвращает True, если t есть подмножество (несобственное) s. Эквивалент t <= s |
| s.pop() | Удаляет случайный элемент s и возвращает его. Возбуждает исключение KeyError, если множесво пустое |
| s.remove(x) | Удаляет элемент x из множества s. Возбуждает исключение KeyError, если x не входит в s |
| s.symmetric_difference(t) | Возвращает новое множество, которое содержит элементы, которые либо содержатся в s и не содержатся в t, либо содержатся в t и не содержатся в s. Эквивалент s ^ t |
| s.symmetric_difference_update(t) | Записывает в s симметрическую разность s и t. Эквивалент s ^= t |
| s.union(t) | Возвращает новое множество, которое содержит все элементы множеств s и t |
| s.update(t) | Добавляет все элементы множества t во множество s |
Последние два метода эквивалентны s | t и s |= t соответственно.
#### Генераторы множеств
В целом то же, что и для списков, но сам генератор обозначается фигурными скобками:
```python
print({x for x in range(10)})
print({2*x for x in range(10)})
print({2*x for x in range(10) if x % 3 == 0})
```
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}
{0, 18, 12, 6}
### Раздел 3. Типы-отображения
Типы отображения — наборы пар "ключ - значение". Единственным встроенным типом-отображением является dictionary, который отображает хешируемые объекты на ссылки на другие объекты.
#### Словари
Тип dict (словарь) это упорядоченная коллекция пар "ключ - значение". Раньше dict был неупорядоченным типом, поэтому во избежания ошибок при исполнении кода более старыми интерпретаторами, советую вам придерживаться отношения к словорям как к неупорядоченным последовательностям.
Словари поддерживают оператор вхождения in, функцию len(), и доступ как к итератору. Среди операций сравнения поддерживаются только == и !=.
Как и обычно, тип dict может быть вызван как функция. Если ей не переданы аргументы, то создается пустой словарь. Если передан аргумент с типом-отображением, то создается словарь на основе этого аргумента. Например, если аргумент типа dict, то будет создана его поверхностная копия. Можно также передать аргумент-последовательность, каждый элемент в котором — последовательность из двух элементов: ключа и значения. Также пустой словарь можно создать при помощи конструкции {}, а словарь с элементами — перечислив пары ключ - значение в фигурных скобках через запятую, отделяя ключи от значений двоеточием:
{ключ1 : значение1, ключ2 : значение2}
К тому же доступны генераторы словарей.
Мы можем доступаться к элементам словаря, используя ключ: d[ключ] вернет значение элемента с ключем *ключ*.
Для добавления новых элементов используется оператор присваивания: d[новый\_ключ] = новое\_значение.
Для типа dict доступны следующие методы:
| Метод | Описание |
| ----- | :------- |
| d.clear() | Удаляет все элементы словаря d |
| d.copy() | Возвращает поверхностную копию словаря d |
| d.fromkeys(s, v) | Возвращает словарь, где ключами являются элементы последователности s, а значения равны v или None, если v не указано |
| d.get(k) | Возвращает значение, которое соответствует ключу k или None, если такого нет |
| d.get(k, v) | Возвращает значение, которое соответствует ключу k или v, если такого нет |
| d.items() | Возвращает view (read-only итератор), содержащий пары (ключ, значение) словаря d |
| d.keys() | Возвращает view, содержащий ключи словаря d |
| d.pop(k) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возбуждается исключение KeyError |
| d.pop(k, v) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возвращается значение v |
| d.popitem() | Удаляет из словаря d и возвращает некоторую пару (ключ, значение). Возбуждает исключение KeyError, если d пуст |
| d.setdefault(k, v) | ТТо же, что и d.get(k), но если такого элемента нет, то создается новый со значением None или v, если указано |
| d.values() | Возвращает view всех значений словаря d |
#### Генераторы словарей
Генераторы словарей имеют две уже знакомые нам формы:
[выражение\_ключа : выражение\_значения for ключ, значение in итератор]
[выражение\_ключа : выражение\_значения for ключ, значение in итератор if условие]
### Раздел 4. Итераторы. Функции и операци для работы с итераторами
Тип данных называется итерируемым, если он может возвращать свои элементы по одному. Технически, любой объект, реализующий метод \_\_iter\_\_() является итерируемым и может предоставить итератор. Итератор — объект, реализуюзий метод \_\_next()\_\_, возвращающий следующее значение и возбуждающий исключение StopIteration, если элементов больше нет.
Для итерируемых объектов доступны следующие функции и операторы:
| Функция или оператор | Описание |
| -------------------- | :------- |
| s + t | Возвращает конкатенацию последовательностей s и t |
| s * n | Возвращает последовательность, которая является результатом повторения последовательности s n раз |
| x in t | Проверяет, есть ли в последовательности i элемент x |
| all(i) | Возвращает True, если все элементы i при приведении к типу bool имеют значение True |
| any(i) | Возвращает True, если один из элементов i при приведении к типу bool имеет значение True |
| enumerate(i, start) | Возвращает последовательность элементов (индекс, значение) для итератора i, начиная с индекса 0 или start, если данный аргумент задан |
| len(x) | Возвращает число элементов в x |
| max(i, key) | Возвращает максимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key максимален |
| min(i, key) | Возвращает минимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key минимален |
| range(start, stop, step) | Возвращает итератор, содержащий целые числа от start до stop с шагом step. Аргумент start включен в последовательность, а stop — нет |
| reversed(i) | Возвращает итератор, в котором элементы идут в обратном порядке |
| sorted(i, key, reverse) | Возвращает отсортированный по неубыванию итератор. Если задан аргумент key, то сортировка идет по значением key(x) для каждого элемента x. Если reversre указан как True, то сортировка идет по невозрастанию |
| sum(i, start) | Возвращает сумму элементов в итераторе i плюс start. Если start не задан, то принимается равным 0 |
| zip(i1, ..., iN) | Возвращает итератор из кортежей, состоящий из элементов i1, ..., iN. Количество кортежей определяется минимальной длиной i1, ..., iN |
### Раздел 5. Копирование коллекций
При присваивании объектов будет выполнено присваивание по ссылке:
```python
x = ['Nightwing', 'Robin', 'Catwoman']
y = x
x[0] = 'Alfred'
print(y)
```
['Alfred', 'Robin', 'Catwoman']
Для создания поверхностной копии можно использовать оператор среза:
```python
x = ['Nightwing', 'Robin', 'Catwoman']
y = x[:]
x[0] = 'Alfred'
print(y)
```
['Nightwing', 'Robin', 'Catwoman']
Но поверхностная копия имеет ограничения, связанные с тем, что при ее создании создаются копии лишь самих элементов. Если они в свою очередь являются ссылками, то мы снова столкнемся с проблемой:
```python
dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]
villains = dc_villains[:]
dc_villains[1][1] = 'Pinguin'
print(villains)
```
[['Lex Luther', 'General Zod'], ['Joker', 'Pinguin']]
Чтобы избежать этого, необходимо использовать функцию deepcopy() модуля copy, которая создает глубокую копию объекта:
```python
from copy import deepcopy
dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]
villains = deepcopy(dc_villains)
dc_villains[1][1] = 'Pinguin'
print(villains)
```
[['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]
### Раздел 6. Практика: vedis и matplotlib
Есть достаточно интересный модуль vedis, который предоставляет доступ к key-value store типа Redis на диске, но при этом API этого доступа совпадает с доступом к словарю.
Решим с использованием модуля vedis [задачу о гипотезе Колатца](https://lambda-it.ru/post/26) и сделаем визуализацию при помощи matplotlib.
Для начала напишем функцию, которая из текущего числа получает следующее в последовательности:
```python
def next_kolatz(x):
if x & 1:
return 3*x + 1
return x >> 1
```
Если число x является нечетным (последний разряд в двоичной системе равен единице), то мы возвращаем 3x + 1. В противном случае, мы делим число на 2 (соответствует сдвигу вправо на один разряд в двоичной системе счисления). Теперь импортируем модуль vedis. При помощи него мы будем хранить длину цепочки для чисел, которые мы уже встречали. Также напишем функцию, которая вычисляет и возвращает длину цепочки для заданного числа x, записывает ее в хранилище, а также записывает в хранилище длины цепочек для всех промежуточных чисел:
```python
def get_kolatz_length(x, storage):
cur = x
try:
return int(storage.get(cur))
except KeyError as e:
pass
stack = [cur]
while True:
cur = next_kolatz(cur)
try:
length = int(storage.get(cur))
break
except KeyError as e:
pass
stack.append(cur)
for item in stack[::-1]:
length += 1
storage.set(item, length)
return int(storage.get(x))
```
Теперь напишем функцию, которая возвращает число, меньшее заданного, для которого данная цепочка максимальна и длину цепочки:
```python
def get_max_kolatz_length(n, storage):
num = max(range(1, n+1), key=lambda x: get_kolatz_length(x, storage))
return num, get_kolatz_length(num, storage)
```
Теперь остается только инициализировать хранилище и испытать наши функции:
```python
from vedis import Vedis
db = Vedis('storage.db')
db.set(1, 1)
print(get_max_kolatz_length(100000, db))
db.close()
```
(77031, 351)
True
Теперь построим обычный scatter plot при помощи matplotlib:
```python
import matplotlib.pyplot as plt
db = Vedis('storage.db')
X = range(1, 101)
Y = [get_kolatz_length(x, db) for x in X]
db.close()
plt.scatter(X, Y)
plt.title('Визуализация длин цепочек Колатца')
plt.xlabel('Число')
plt.ylabel('Длина цепочки')
plt.show()
```

### Раздел 7. Домашнее задание
Решить задачи после третьей главы Саммерфилда
Загуглить гипотезу Гольдбаха
Загуглить динамическое программирование
Написать функцию, которая принимает в качестве аргумента множество и функцию и при помощи matplotlib изображает график заданной функции на множестве значений. Добавьте несколько дополнительных аргументов: например, возможность сделать одну из осей логарифмической, выбрать разные цветовые схемы, разные типы графиков и т.д.
Решить задачу, которая будет опубликована в субботу