PythonCore Flashcards

(54 cards)

1
Q

Что такое последовательность

A

Последовательностью в Python называется итерабельный объект, который поддерживает эффективный доступ к элементам с использованием целочисленных индексов через специальный метод __getitem__() и поддерживает метод __len__(), который возвращает длину последовательности. К основным встроенным типам последовательностей относятся list, tuple, range, str и bytes.

Последовательности также опционально могут реализовывать методы count(), index(), __contains__() и __reversed__() и другие.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Какие операции поддерживают большинство последовательностей

A

x in s, x not in s – находится ли элемент x в последовательности s (для строк и последовательностей байтов – является ли x подстрокой s)
s + t – конкатенация последовательностей s и t
s * n, n * s – конкатенация n нерекурсивных копий последовательности s
s[i] – i-й элемент последовательности s
s[:i] - срез всех элементов последовательности s до i (не включая i)
s[i:] - срез всех элементов последовательности s от i до последнего элемента
s[-i:] - срез последних i элементов последовательности s
s[::-1] - перевернуть последовательность
s[i:j] – срез последовательности s от i до j (не включая j)
s[i:j:k] – срез последовательности s от i до j с шагом k (не включая j)
len(s) – длина последовательности s
min(s) – минимальный элемент последовательности s
max(s) – максимальный элемент последовательности s
s.index(x[, i[, j]]) – индекс первого вхождения x (опционально – начиная с позиции i и до позиции j)
s.count(x) – общее количество вхождений x в s
sum(s) – сумма элементов последовательности
Неизменяемые последовательности обычно реализуют операцию hash(s) – хеш-значение объекта.

Большинство изменяемых последовательностей поддерживают следующие операции:

s[i] = x – элемент с индексом i заменяется на x
s[i:j] = t, s[i:j:k] = t – элементы с индексами от i до j (с шагом k) заменяются содержимым итерабельного объекта t
del s[i:j], del s[i:j:k] – удаление соответствующих элементов из последовательности
s.append(x) – добавление x в конец последовательности
s.clear() – удаление всех элементов последовательности
s.copy() – нерекурсивная копия последовательности
s.extend(t) – добавление всех элементов итерабельного объекта в конец последовательности
s.insert(i, x) – вставка элемента x по индексу i
s.pop(), s.pop(i) – возврат значения по индексу i (по умолчанию – последний элемент) и удаление его из последовательности
s.remove(x) – удаление первого вхождения x
s.reverse() – разворот последовательности в обратном порядке

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Какие виды строк бывают в питоне

A

Зависит от версии Питона. Во второй ветке два типа: однобайтные строки и Юникод представлены классами str и unicode соответственно. В третьем Питоне есть один вид строк str, который представляет собой Юникод. Однобайтных строк нет, вместо них есть тип bytes, то есть цепочка байт.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Можно ли изменить отдельный символ внутри строки

A

Нет, строки неизменяемы. Операции замены, форматирования и конкатенации возвращают новую строку.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Как соединить список строк в одну. Как разбить строку на список строк

A

Чтобы соединить, нужен метод строки .join(). Чтобы разбить, метод .split().

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Как кодировать и декодировать строки

A

Кодировать – перевести Юникод в байтовую строку. Вызвать метод .encode() у строки.

Декодировать – восстановить строку из цепочки байт. Вызвать метод .decode() у объекта str или bytes (версии Питона 2 и 3 соответственно).

В обоих случаях явно передавать кодировку, иначе будет использована та, что определена в системе по умолчанию. Быть готовым поймать исключения UnicodeEncodeError, UnicodeDecodeError.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Чем список отличается от кортежа

A

Списки – это изменяемые последовательности, обычно используемые для хранения однотипных данных (хотя Python не запрещает хранить в них данные разных типов). Представлены классом list.

Кортежи – это неизменяемые последовательности, обычно используемые, чтобы хранить разнотипные данные. Представлены классом tuple.

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

Существуют специальные функции для работы со списками. Они вызываются методами списка. Ниже приведены наиболее часто используемые.

Создаем исходный список
lst = [1, 2, 3]

append(x): добавляет элемент в конец списка
lst.append(4)
# Теперь lst = [1, 2, 3, 4]

extend(iterable): расширяет список, добавляя элементы из итерируемого объекта
lst.extend([5, 6])
# Теперь lst = [1, 2, 3, 4, 5, 6]

insert(i, x): вставляет элемент x на позицию i
lst.insert(0, ‘start’)
# Теперь lst = [‘start’, 1, 2, 3, 4, 5, 6]

remove(x): удаляет первое вхождение элемента x
lst.remove(3)
# Теперь lst = [‘start’, 1, 2, 4, 5, 6]

pop([i]): удаляет и возвращает элемент на позиции i (по умолчанию последний)
last = lst.pop()
# last = 6, а lst = [‘start’, 1, 2, 4, 5]

sort(): сортирует список на месте
lst = [3, 1, 4, 1, 5, 9, 2]
lst.sort()
# Теперь lst = [1, 1, 2, 3, 4, 5, 9]

reverse(): разворачивает список на месте
lst.reverse()
# Теперь lst = [9, 5, 4, 3, 2, 1, 1]

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Что такое диапазон

A

Диапазоны – неизменяемые последовательности чисел, которые задаются началом, концом и шагом. Представлены классом range (в Python 2 – xrange; range в Python 2 – это функция, которая возвращает список). Параметры конструктора должны быть целыми числами (либо экземпляры класса int, либо любой объект с методом __index__) Поддерживает все общие для последовательностей операции, кроме конкатенации и повторения, а также, в версиях Python до 3.2, срезов и отрицательных индексов.

Как сделать список уникальным (без повторяющихся элементов)
Вариант со множеством. Не гарантирует порядок элементов. Порядок сохраняется только для маленьких списков.

list(set([1, 2, 2, 2, 3, 3, 1]))
»> [1, 2, 3]
Вариант с OrderedDict. Гарантирует порядок элементов.

> > > from collections import OrderedDict
list(OrderedDict.fromkeys([1, 2, 2, 2, 3, 3, 1]))
[1, 2, 3]
Вариант с циклом. Медленно, но гарантирует порядок. Подходит, если элементы нельзя помещать внутрь множества (например, словари).

res = []
for x in [1, 2, 2, 2, 3, 3, 1]:
if x not in res:
res.append(x)
»> [1, 2, 3]

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Есть кортеж из трех элементов. Назначить переменным a, b, c его значения

A

a, b, c = (1, 2, 3)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Как сравниваются последовательности

A

Две последовательности равны, если они имеют одинаковый тип, равную длину и соответствующие элементы обоих последовательностей равны.

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Как понять хешируемый ли объект

A

Объект называется хешируемым, если он имеет хеш-значение (целое число), которое никогда не изменяется на протяжении его жизненного цикла и возвращается методом __hash__(), и может сравниваться с другими объектами (реализует метод __eq__()). Равные хешируемые объекты должны иметь равные хеш-значения. Все стандартные неизменяемые объекты хешируемые. Все стандартные изменяемые объекты не хешируемые.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Что такое множество

A

Множество – это неупорядоченная коллекция хешируемых объектов, которые не повторяются. В множествах нет понятия позиции элемента. Соответственно, они не поддерживают индексацию и срезы. Встроенные классы множеств: set (изменяемое множество), frozenset (неизменяемое множество).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Для чего применяются множества

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Какие операции можно производить над множествами

A

set([iterable]), frozenset([iterable]) – создание множества (пустого или из элементов итерабельного объекта)

len(s) – количество элементов множества

x in s, x not in s – проверка нахождения элемента в множестве

s.isdisjoint(t) – проверка того, что данное множество не имеет общих элементов с заданным

s.issubset(t), s <= t – проверка того, что все элементы множества s являются элементами множества t

s < t – проверка того, что s <= t и s != t

s.isuperset(t), s >= t – проверка того, что все элементы множества t являются элементами множества s

s > t – проверка того, что s >= t и s != t

s.union(t, …), s | t | … – создание нового множества, которое является объединением данных множеств

s.intersection(t, …), s & t & … – создание нового множества, которое является пересечением данных множеств

s.difference(t, …), s - t - … – создание нового множества, которое является разницей данных множеств

s.symmetric_difference(t), s ^ t – создание нового множества, которое является симметрической разницей данных множеств (то есть, разница объединения и пересечения множеств)

s.copy() – неполная копия множества s

Операции над множествами, которые являются методами, принимают в качестве аргументов любые итерабельные объекты. Операции над множествами, записанные в виде бинарных операций, требуют, чтобы второй операнд операции тоже был множеством, и возвращают множество того типа, которым было первое множество.

Операции над изменяемыми множествами:

s.update(t, …), s |= t | … – добавить в данное множество элементы из других множеств

s.intersection_update(t, …), s &= t & … – оставить в данном множестве только те элементы, которые есть и в других множествах

s.difference_update(t, …), s -= t | … – удалить из данного множества те элементы, которые есть в других множествах

s.symmetric_difference_update(t), s ^= t – оставить или добавить в s элементы, которые есть либо в s, либо в t, но не в обоих множествах

s.add(element) – добавить новый элемент в множество

s.remove(element) – удалить элемент из множества; если такого элемента нет, возникает исключение KeyError

s.discard(element) – удалить элемент из множества, если он в нём находится

s.pop() – удалить из множества и вернуть произвольный элемент; если множество пустое, возникает исключение KeyError

s.clear() – удалить все элементы множества.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Как происходит проверка множеств на равенство

A

Проверка множеств на равенство происходит поэлементно, независимо от типов множеств.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Что такое отображение

A

Отображение (mapping) – это объект-контейнер, который поддерживает произвольный доступ к элементам по ключам и описывает все методы, описанные в абстрактном базовом классе collections.Mapping (get(), items(), keys(), values()) или collections.MutableMapping (clear(), get(), items(), keys(), pop(), popitem(), setdefault(), update(), values()). К отображениям относятся классы dict, collections.defaultdict, collections.OrderedDict и collections.Counter

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Какие нюансы есть в использовании чисел как ключей

A

Числовые ключи в словарях подчиняются правилам сравнения чисел. Таким образом, int(1) и float(1.0) считаются одинаковым ключом. Однако из-за того, что значения типа float сохраняются приближенно, не рекомендуется использовать их в качестве ключей.

> > > {True: ‘yes’, 1: ‘no’, 1.0: ‘maybe’}
{True: ‘maybe’}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Какие операции можно производить над отображениями

A

len(d) – количество элементов.

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

__missing__(self, key), то он вызывается. Если ключ не существует и метод

__missing__ не определён, выбрасывается исключение KeyError.

d[key] = value – изменить значение или создать новую пару ключ-значение, если ключ не существует.

key in d, key not in d – проверка наличия ключа в отображении.

iter(d) – то же самое, что iter(d.keys()).

clear() – удалить все элементы словаря.

copy() – создать неполную копию словаря.

(метод класса) dict.fromkeys(sequence[, value]) – создаёт новый словарь с ключами из последовательности sequence и заданным значением (по умолчанию – None).

d.get(key[, default]) – безопасное получение значения по ключу (никогда не выбрасывает KeyError). Если ключ не найден, возвращается значение default (по-умолчанию – None).

d.items() – в Python 3 возвращает объект представления словаря, соответствующий парам (двухэлементным кортежам) вида (ключ, значение). В Python 2 возвращает соответствующий список, а метод iteritems() возвращает итератор. Аналогичный метод в Python 2.7 – viewitems().

d.keys() – в Python 3 возвращает объект представления словаря, соответствующий ключам словаря. В Python 2 возвращает соответствующий список, а метод iterkeys() возвращает итератор. Аналогичный метод в Python 2.7 – viewkeys().

d.pop(key[, default]) – если ключ key существует, удаляет элемент из словаря и возвращает его значение. Если ключ не существует и задано значение default, возвращается данное значение, иначе выбрасывается исключение KeyError.

d.popitem() – удаляет произвольную пару ключ-значение и возвращает её. Если словарь пустой, возникает исключение KeyError. Метод полезен для алгоритмов, которые обходят словарь, удаляя уже обработанные значения (например, определённые алгоритмы, связанные с теорией графов).

d.setdefault(key[, default]) – если ключ key существует, возвращает соответствующее значение. Иначе создаёт элемент с ключом key и значением default. default по умолчанию равен None.

d.update(mapping) – принимает либо другой словарь или отображение, либо итерабельный объект, состоящий из итерабельных объектов – пар ключ-значение, либо именованные аргументы. Добавляет соответствующие элементы в словарь, перезаписывая элементы с существующими ключами.

d.values() – в Python 3 возвращает объект представления словаря, соответствующий значениям. В Python 2 возвращает соответствующий список, а метод itervalues() возвращает итератор. Аналогичный метод в Python 2.7 – viewvalues().

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Что возвращает метод items

A

Объекты, возвращаемые методами items(), keys() и values() (viewitems(), viewkeys(), viewvalues() в Python 2.7) – это объекты представления словаря. Они предоставляют динамическое представление элементов словаря, то есть изменения данного словаря автоматически отображаются и на этих объектах.

Операции с представлениями словарей:

iter(dictview) – получение итератора по ключам, значениям или парам ключей и значений. Все представления словарей при итерировании возвращают элементы словаря в одинаковом порядке. При попытке изменить словарь во время итерирования может возникнуть исключение RuntimeError
len(dictview) – количество элементов в словаре.
x in dictview – проверка существования ключа, значения или пары ключ-значение в словаре.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Как отсортировать список словарей по определенному полю

A

Метод списка .sort() и встроенная функция sorted() принимают параметр key. Им должен быть вызываемый объект, который принимает очередной элемент (в нашем случае словарь) и возвращает значение-критерий сортировки.

Код ниже показывает, как отсортировать список людей по возрасту:

users = [{‘age’: 30}, {‘age’: 20}, {‘age’: 10}]
users.sort(key=lambda user: user[‘age’])
»> [{‘age’: 10}, {‘age’: 20}, {‘age’: 30}]

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Что может являться ключом словаря. Что не может. Почему

A

Ключом словаря может быть любой хешируемый неизменяемый объект: число, строка, datetime, функция и даже модуль. Такие объекты имеют метод __hash__(), который однозначно сопоставляет объект с некоторым числом. По этому числу словарь ищет значение для ключа.

Списки, словари и множества изменяемы и не имеют метода хеширования. При подстановке их в словарь возникнет ошибка.

Хеш кортежа вычисляется рекурсивно по всем элементам. Так, кортеж

(1, (True, (42, (‘hello’, )))) состоит только из неизменяемых элементов, поэтому может быть ключом. Однако, такой кортеж

(1, (True, (42, ({‘hello’: ‘world’}, )))) содержит глубоко внутри словарь, поэтому хеш не может быть рассчитан.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Есть два списка – ключи и значения. Как составить из них словарь

A

keys = [‘foo’, ‘bar’, ‘baz’]
vals = [1, 2, 3]
dict(zip(keys, vals))
»> {‘baz’: 3, ‘foo’: 1, ‘bar’: 2}
Функция zip отдает список пар N-ых элементов. Конструктор dict принимает список пар. Каждую пару он рассматривает как ключ и значение соответственно.

23
Q

Как работает хэш-таблица

A

Хэш-таблица это разреженный массив (массив, в котором имеются незаполненные позиции). В стандартных англоязычных учебниках ячейки хэш-таблицы называются “bucket”. В хэш-таблице dict каждому элементу соотвествует ячейка, содержащая два поля: ссылку на ключ и ссылку на значение элемента. Поскольку размер всех ячеек одинаков, доступ к отдельной ячейке производится по смещению.

Python стремится оставить не менее трети ячеек пустыми; если хэш-таблица становится чрезмерно заполненной, то она копируется в новый участок памяти, где есть место для большего числа ячеек.

Для помещения элемента в хэш-таблицу нужно первым делом вычислить хэш-значение ключа элемента. Это делает встроенная функция hash().

Для выборки значения с помощью выражения my_dict[search_key] Python обращается к функции hash(search_key), чтобы получить хэш-значение search_key, и использует несколько младших битов полученного числа как смещение ячейки относительно начала хэш-таблицы (сколько именно битов зависит от текущего размера таблицы). Если найденная ячейка пуста, возбуждается исключение KeyError. В противном случае в найденной ячейке есть какой-то элемент - пара ключ:значение - и тогда Python проверяет, верно ли то, что search_key == found_key. Если да, то элемент найден и возвращается found_value. Если же search_key и found_key не совпали, то имеет место коллизия хэширования. Для разрешения коллизии алгоритм берет различные биты хэш-значения, производит над ними определенные действия и использует результат как смещение другой ячейки.

24
Q

Что такое коллизия

A

Когда хеш-функция возвращает один и тот же ответ для разных данных.

25
Что такое хеш-функция
Хеш-функция (или хеширующая функция, функция свёртки) — это математическая функция, которая преобразует входные данные (любого размера) в выходные данные фиксированного размера, называемые хешем, хеш-значением, хеш-кодом или дайджестом сообщения. Этот процесс называется хешированием. Представьте себе, что у вас есть огромная книга, и вы хотите быстро найти, содержится ли в ней какое-то конкретное предложение. Вместо того чтобы перечитывать всю книгу, вы можете использовать хеш-функцию, чтобы "сжать" каждое предложение в короткий, уникальный "отпечаток" (хеш). Затем вы можете сравнить отпечаток вашего искомого предложения с отпечатками всех предложений в книге. Основные характеристики и свойства хеш-функций: Детерминированность: Для одних и тех же входных данных хеш-функция всегда должна производить один и тот же хеш. Это критически важно для предсказуемости и надежности. Эффективность вычисления: Вычисление хеша для любых входных данных должно быть быстрым и эффективным. Выход фиксированного размера: Независимо от размера входных данных (будь то один байт или гигабайт), выходное хеш-значение всегда имеет фиксированную длину (например, 128 бит, 256 бит и т.д.). Лавинный эффект (Avalanche Effect): Небольшое изменение во входных данных (даже один бит) должно приводить к значительному изменению хеш-значения. Это свойство делает хеш-функции устойчивыми к подбору входных данных для получения желаемого хеша. Однонаправленность (Preimage Resistance): Должно быть вычислительно невозможно восстановить исходные входные данные, зная только их хеш-значение. Это как узнать, что у вас есть отпечаток пальца, но не иметь возможности восстановить сам палец. Устойчивость к коллизиям (Collision Resistance): Должно быть вычислительно невозможно найти две различные входные данные, которые производят одинаковое хеш-значение. Это самое важное свойство для криптографических хеш-функций. Слабая устойчивость к коллизиям (Second Preimage Resistance): Зная одно входное значение X_1, трудно найти другое значение X_2 такое, что H(X_1)=H(X_2). Сильная устойчивость к коллизиям (Collision Resistance): Трудно найти любые два различных значения X_1 и X_2 такие, что H(X_1)=H(X_2). Примеры хеш-функций: CRC (Cyclic Redundancy Check): Используется для обнаружения ошибок в данных, но не является криптографической. MD5 (Message-Digest Algorithm 5): Ранее широко использовался для криптографических целей, но сейчас считается небезопасным из-за найденных коллизий. SHA-1 (Secure Hash Algorithm 1): Также имеет известные уязвимости. SHA-2 (SHA-256, SHA-512): Семейство криптографически стойких хеш-функций, широко используемых сегодня. SHA-3 (Keccak): Новое поколение криптографических хеш-функций, разработанное как альтернатива SHA-2. Как это выглядит в Python? Python имеет встроенную поддержку хеширования (для неизменяемых объектов) и модуль hashlib для криптографических хеш-функций. Встроенная хеш-функция (hash()): Python print(hash("привет")) # Выдаст целое число print(hash(123)) # Выдаст целое число print(hash((1, 2, 3))) # Выдаст целое число Изменяемые объекты не хешируются: # hash([1, 2, 3]) # Вызовет TypeError
26
Для чего используются хеш-функции?
Хеш-функции применяются во многих областях компьютерных наук и инженерии: Структуры данных (Хеш-таблицы / Словари): Это одно из самых распространенных применений. Хеш-таблицы используют хеш-функции для быстрого хранения и извлечения данных. Ключ элемента хешируется, и полученное хеш-значение указывает на место хранения элемента в таблице. Пример в Python: dict (словарь) использует хеширование для своих ключей, что обеспечивает очень быструю среднюю скорость доступа O(1). Проверка целостности данных: Хеш-значение файла или сообщения может быть вычислено и сохранено. Позже, чтобы проверить, не был ли файл изменен (поврежден или подделан), можно повторно вычислить его хеш и сравнить с исходным. Если хеши не совпадают, данные были изменены. Примеры: контрольные суммы MD5, SHA-256 для проверки загруженных файлов. Криптография (Цифровые подписи, пароли): Хранение паролей: Вместо того чтобы хранить пароли в открытом виде, хранят их хеши. При входе пользователя его введенный пароль хешируется и сравнивается с сохраненным хешем. Если хеши совпадают, пароль верен. Это защищает пароли в случае взлома базы данных. Цифровые подписи: Хеш документа подписывается закрытым ключом отправителя, что обеспечивает аутентичность и целостность документа. Блокчейн и криптовалюты: В блокчейне хеш-функции используются для связывания блоков (хеш предыдущего блока включается в текущий блок) и для обеспечения безопасности транзакций (доказательство работы). Обнаружение дубликатов: Хеширование может использоваться для быстрого выявления дублирующихся файлов или записей в больших наборах данных.
27
Где будет быстрее поиск, а где перебор и почему: dict, list, set, tuple
Поиск будет быстрее в dict и set, потому что это хэш-таблицы, доступ к элементу которых выполняется за O(1). Для list и tuple поиск будет выполняться в среднем за O(n). Исключение работает только для очень маленьких списков длиной до 5 элементов. В этом случае интерпретатору будет быстрей пробежаться по списку, чем считать хеш. В Python 2 методы словаря keys, values, items возвращают список. Тоесть перед итерацией по словарю (или сету) интерпретатор сначала создает новый список, что занимает дополнительное время и память, но после создания это уже обыкновенный список. Тоесть в Python 2 итерация по словарям и сетам выполняется дольше, за счет создания нового списка и копирования в него элементов. В Python 3 эти методы создают объект-представление. Это определенно происходит быстрее чем создание нового списка в Python2. Но итерирование по такому представлению должно происходить немного дольше, чем по списку из-за того что данные в словарях хранятся разреженно (редко, негусто). В подтверждение вышесказанного (Python 3): >>> l = list(range(1000000)) >>> d = dict.fromkeys(l) >>> s = set(l) >>> def iter_list(): ... for i in l: ... pass ... >>> def iter_dict(): ... for i in d: ... pass ... >>> def iter_set(): ... for i in s: ... pass ... >>> timeit.timeit(iter_list, number=1000) 6.727667486004066 >>> timeit.timeit(iter_dict, number=1000) 9.293120226997416 >>> timeit.timeit(iter_set, number=1000) 8.627948219014797
28
Что такое args, kwargs. В каких случаях они требуются
Выражения *args и **kwargs объявляют в сигнатуре функции. Они означают, что внутри функции будут доступны переменные с именами args и kwargs (без звездочек). Можно использовать другие имена, но это считается дурным тоном. args – это кортеж, который накапливает позиционные аргументы. kwargs – словарь именованных аргументов, где ключ – имя параметра, значение – значение параметра. Важно: если в функцию не передано никаких параметров, переменные будут соответственно равны пустому кортежу и пустому словарю, а не None.
29
Почему использовать изменяемые объекты как параметры по-умолчанию плохо. Приведите пример плохого случая. Как исправить
Функция создается однажды при загрузке модуля. Именованные параметры и их дефолтные значения тоже создаются один раз и хранятся в одном из полей объекта-функции. В нашем примере bar равен пустому списку. Список – изменяемая коллекция, поэтому значение bar может изменяться от вызова к вызову. Пример: def foo(bar=[]): bar.append(1) return bar foo() >>> [1] foo() [1, 1] foo() >>> [1, 1, 1] Хорошим тоном считается указывать параметру пустое неизменяемое значение, например 0, None, '', False. В теле функции проверять на заполненность и создавать новую коллекцию: def foo(bar=None): if bar is None: bar = [] bar.append(1) return bar foo() >>> [1] foo() >>> [1] foo() >>> [1] Сказанное выше актуально в т.ч. для множеств и словарей.
30
Можно ли передавать функцию в качестве аргумента другой функции
Можно, функция в Питоне объект первого порядка: допускает присваивание, передачу в функцию, удаление.
31
Можно ли объявлять функцию внутри другой функции. Где она будет видна
Можно. Такая функция будет видна только внутри первой функции
32
Что такое лямбды. Каковы их особенности
Это анонимные функции. Они не резервируют имени в пространстве имен. Лямбды часто передают в функции map, reduce, filter. Лямбды в Питоне могут состоять только из одного выражения. Используя синтаксис скобок, можно оформить тело лямбды в несколько строк. Использовать точку с запятой для разделения операторов нельзя.
33
Когда лучше и зачем использовать лямбда-функции в сравнении с обычными функциями?
Лямбда-функции в Python (также известные как анонимные функции) — это небольшие, однострочные функции, которые не имеют имени. Они определяются с помощью ключевого слова lambda. В отличие от обычных функций, определенных с помощью def, лямбда-функции имеют некоторые ограничения, но предлагают преимущества в определенных сценариях. Синтаксис: Python lambda аргументы: выражение аргументы: Список аргументов, разделенных запятыми. выражение: Единственное выражение, которое будет вычислено и возвращено. Лямбда-функция не может содержать несколько выражений или операторов (например, if, for, while). Сравнения лямбда функции и обычной: Анонимная (без имени) -> Имеет имя lambda аргументы: -> выражение def имя_функции(аргументы): ... Одно выражение, результат которого возвращается неявно -> Может содержать несколько выражений, операторов, циклов, условий. Явный return. Результат вычисления выражения -> Может возвращать что угодно, или ничего (тогда возвращает None) Для коротких, одноразовых функций -> Для более сложных, многократно используемых задач Иногда улучшает читаемость для простых задач, иногда ухудшает для сложных -> Как правило, более читаема для любых задач
34
Допустимы ли следующие выражения
nope = lambda: pass riser = lambda x: raise Exception(x) Нет, при загрузке модуля выскочит исключение SyntaxError. В теле лямбды может быть только выражение. pass и raise являются операторами.
35
Как передаются значения аргументов в функцию или метод
В таких языках как C++ есть переменные, хранящиеся на стеке и в динамической памяти. При вызове функции мы помещаем все аргументы на стек, после чего передаём управление функции. Она знает размеры и смещения переменных на стеке, соответственно может их правильно интерпретировать. При этом у нас есть два варианта: скопировать на стек память переменной или положить ссылку на объект в динамической памяти (или на более высоких уровнях стека). Очевидно, что при изменении значений на стеке функции, значения в динамической памяти не поменяются, а при изменении области памяти по ссылке, мы модифицируем общую память, соответственно все ссылки на эту же область памяти «увидят» новое значение. В python отказались от подобного механизма, заменой служит механизм связывания (assignment) имени переменной с объектом, например при создании переменной: var = "john" Интерпретатор создаёт объект «john» и «имя» var, а затем связывает объект с данным именем. При вызове функции, новых объектов не создаётся, вместо этого в её области видимости создаётся имя, которое связывается с существующим объектом. Но в python есть изменяемые и неизменяемые типы. Ко вторым, например, относятся числа: при арифметических операциях существующие объекты не меняются, а создаётся новый объект, с которым потом связывается существующее имя. Если же со старым объектом после этого не связано ни одного имени, оно будет удалено с помощью механизма подсчёта ссылок. Если же имя связано с переменной изменяемого типа, то при операциях с ней изменяется память объекта, соответственно все имена, связанные с данной областью памяти «увидят» изменения.
36
Что такое замыкание
Синтаксически это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней.
37
Итераторы и Генераторы в Python
Итераторы и генераторы — это мощные инструменты в Python, которые позволяют эффективно работать с коллекциями данных, особенно большими, обеспечивая при этом хорошую производительность и экономию памяти. Они тесно связаны с концепцией ленивых вычислений, когда значения генерируются по требованию, а не все сразу. Итераторы Что такое итератор? Итератор — это объект, который представляет собой поток данных. Он позволяет вам проходить по элементам коллекции один за другим, не загружая всю коллекцию в память целиком. Это особенно полезно при работе с большими наборами данных, файлами или бесконечными последовательностями. Как работают итераторы? Объект считается итератором, если он реализует два специальных метода: __iter__(): Должен возвращать сам объект итератора. __next__(): Должен возвращать следующий элемент из последовательности. Когда элементов больше нет, он должен вызвать исключение StopIteration. Примеры использования: Многие встроенные типы в Python, такие как списки, кортежи, строки, словари и множества, являются итерируемыми (iterable). Это значит, что вы можете получить из них итератор с помощью функции iter(): ``` my_list = [1, 2, 3] my_iterator = iter(my_list) print(next(my_iterator)) # Вывод: 1 print(next(my_iterator)) # Вывод: 2 print(next(my_iterator)) # Вывод: 3 # print(next(my_iterator)) # Вызовет StopIteration ``` Цикл for ... in ... в Python под капотом работает именно с итераторами: он вызывает iter() для получения итератора и затем многократно вызывает next() до получения StopIteration. Генераторы Что такое генератор? Генератор — это специальный тип итератора, который позволяет создавать итераторы простым и элегантным способом. Генераторные функции выглядят как обычные функции, но вместо ключевого слова return они используют yield для возврата значений. Как работают генераторы? Когда функция-генератор вызывается, она не начинает выполнять код сразу. Вместо этого она возвращает объект-генератор (который сам по себе является итератором). Код внутри функции-генератора выполняется только тогда, когда у объекта-генератора запрашивается следующий элемент (например, с помощью next() или в цикле for). При встрече с оператором yield, функция "ставится на паузу", сохраняя своё состояние, и возвращает значение. При следующем вызове next(), она возобновляет выполнение с того места, где остановилась, и продолжает до следующего yield или до завершения функции. Примеры использования: Python def my_generator_function(): print("Начинаем генерацию") yield 1 print("Продолжаем генерацию") yield 2 print("Заканчиваем генерацию") yield 3 Создаем объект-генератор gen = my_generator_function() print("Первый вызов next():") print(next(gen)) # Вывод: Начинаем генерацию, затем 1 print("Второй вызов next():") print(next(gen)) # Вывод: Продолжаем генерацию, затем 2 print("Третий вызов next():") print(next(gen)) # Вывод: Заканчиваем генерацию, затем 3 print(next(gen)) # Вызовет StopIteration Генераторные выражения (Generator Expressions): Для простых генераторов существует компактный синтаксис, похожий на списковые включения, но использующий круглые скобки вместо квадратных: Python Генераторное выражение, которое не создает список в памяти сразу squares_gen = (x * x for x in range(1000000)) Можно использовать в цикле for или с next() for i, sq in enumerate(squares_gen): if i < 5: print(sq) else: break Основные преимущества и различия Характеристика Итераторы (общий концепт) Генераторы (способ создания итераторов) Определение Объект, реализующий __iter__() и __next__() Функция, использующая yield, или генераторное выражение Создание Через iter() из итерируемого объекта, или вручную классами С помощью def + yield или генераторных выражений Состояние Управляется вручную в __next__() Автоматически сохраняется и восстанавливается Python-интерпретатором Применение Ленивая обработка данных, обход коллекций Упрощенное создание пользовательских итераторов, эффективная генерация последовательностей Ответ для интервью: Итераторы и Генераторы Привет! Конечно, давайте подготовим хороший ответ для интервью по итераторам и генераторам. Это фундаментальные концепции в Python, понимание которых показывает ваш уровень владения языком. Итераторы и Генераторы в Python Итераторы и генераторы — это мощные инструменты в Python, которые позволяют эффективно работать с коллекциями данных, особенно большими, обеспечивая при этом хорошую производительность и экономию памяти. Они тесно связаны с концепцией ленивых вычислений, когда значения генерируются по требованию, а не все сразу. Итераторы Что такое итератор? Итератор — это объект, который представляет собой поток данных. Он позволяет вам проходить по элементам коллекции один за другим, не загружая всю коллекцию в память целиком. Это особенно полезно при работе с большими наборами данных, файлами или бесконечными последовательностями. Как работают итераторы? Объект считается итератором, если он реализует два специальных метода: __iter__(): Должен возвращать сам объект итератора. __next__(): Должен возвращать следующий элемент из последовательности. Когда элементов больше нет, он должен вызвать исключение StopIteration. Примеры использования: Многие встроенные типы в Python, такие как списки, кортежи, строки, словари и множества, являются итерируемыми (iterable). Это значит, что вы можете получить из них итератор с помощью функции iter(): Python my_list = [1, 2, 3] my_iterator = iter(my_list) print(next(my_iterator)) # Вывод: 1 print(next(my_iterator)) # Вывод: 2 print(next(my_iterator)) # Вывод: 3 # print(next(my_iterator)) # Вызовет StopIteration Цикл for ... in ... в Python под капотом работает именно с итераторами: он вызывает iter() для получения итератора и затем многократно вызывает next() до получения StopIteration. Генераторы Что такое генератор? Генератор — это специальный тип итератора, который позволяет создавать итераторы простым и элегантным способом. Генераторные функции выглядят как обычные функции, но вместо ключевого слова return они используют yield для возврата значений. Как работают генераторы? Когда функция-генератор вызывается, она не начинает выполнять код сразу. Вместо этого она возвращает объект-генератор (который сам по себе является итератором). Код внутри функции-генератора выполняется только тогда, когда у объекта-генератора запрашивается следующий элемент (например, с помощью next() или в цикле for). При встрече с оператором yield, функция "ставится на паузу", сохраняя своё состояние, и возвращает значение. При следующем вызове next(), она возобновляет выполнение с того места, где остановилась, и продолжает до следующего yield или до завершения функции. Примеры использования: Python def my_generator_function(): print("Начинаем генерацию") yield 1 print("Продолжаем генерацию") yield 2 print("Заканчиваем генерацию") yield 3 Создаем объект-генератор gen = my_generator_function() print("Первый вызов next():") print(next(gen)) # Вывод: Начинаем генерацию, затем 1 print("Второй вызов next():") print(next(gen)) # Вывод: Продолжаем генерацию, затем 2 print("Третий вызов next():") print(next(gen)) # Вывод: Заканчиваем генерацию, затем 3 print(next(gen)) # Вызовет StopIteration Генераторные выражения (Generator Expressions): Для простых генераторов существует компактный синтаксис, похожий на списковые включения, но использующий круглые скобки вместо квадратных: Python Генераторное выражение, которое не создает список в памяти сразу squares_gen = (x * x for x in range(1000000)) Можно использовать в цикле for или с next() for i, sq in enumerate(squares_gen): if i < 5: print(sq) else: break Основные преимущества и различия Характеристика Итераторы (общий концепт) Генераторы (способ создания итераторов) Определение Объект, реализующий __iter__() и __next__() Функция, использующая yield, или генераторное выражение Создание Через iter() из итерируемого объекта, или вручную классами С помощью def + yield или генераторных выражений Состояние Управляется вручную в __next__() Автоматически сохраняется и восстанавливается Python-интерпретатором Применение Ленивая обработка данных, обход коллекций Упрощенное создание пользовательских итераторов, эффективная генерация последовательностей Export to Sheets Ключевые преимущества использования итераторов и генераторов: Экономия памяти (Memory Efficiency): Они генерируют элементы по одному по мере необходимости, а не хранят всю последовательность в памяти. Это критически важно для работы с очень большими или бесконечными последовательностями данных. Производительность: Хотя запуск генератора может быть немного медленнее, чем создание полного списка, общая производительность может быть выше за счет снижения потребления памяти и избегания лишних операций. Легкость чтения и написания кода: Генераторы позволяют писать чистый и компактный код для создания сложных итераций. Бесконечные последовательности: Только генераторы могут легко представлять бесконечные последовательности данных, поскольку они никогда не пытаются вычислить все элементы сразу. Заключение Итераторы и генераторы — это краеугольные камни идиоматического Python-кода, особенно в контексте обработки данных и разработки эффективных приложений. Понимание того, когда и как их использовать, позволяет писать более масштабируемый, производительный и чистый код. Они особенно полезны в задачах, связанных с парсингом файлов, потоковой обработкой данных, асинхронным программированием и любыми сценариями, где полная загрузка данных в память нежелательна или невозможна.
38
Что такое контейнер
Контейнер – это тип данных, который инкапсулирует в себе значения других типов. Списки, кортежи, множества, словари и т.д. являются контейнерами.
39
Что такое итерабельный объект
Итерабельный объект (в оригинальной терминологии – «iterable») – это объект, который может возвращать значения по одному за раз. Примеры: все контейнеры и последовательности (списки, строки и т.д.), файлы, а также экземпляры любых классов, в которых определён метод __iter__() или __getitem__(). Итерабельные объекты могут быть использованы внутри цикла for, а также во многих других случаях, когда ожидается последовательность (функции sum(), zip(), map() и т.д.). Подробнее: Рассмотрим итерируемый объект (Iterable). В стандартной библиотеке он объявлен как абстрактный класс collections.abc.Iterable: class Iterable(metaclass=ABCMeta): __slots__ = () @abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, C): if cls is Iterable: return _check_methods(C, "__iter__") return NotImplemented У него есть абстрактный метод __iter__ который должен вернуть объект итератора. И метод __subclasshook__ который проверяет наличие у класса метод __iter__. Таким образом, получается, что итерируемый объект это любой объект который реализует метод __iter__ class SomeIterable1(collections.abc.Iterable): def __iter__(self): pass class SomeIterable2: def __iter__(self): pass print(isinstance(SomeIterable1(), collections.abc.Iterable)) # True print(isinstance(SomeIterable2(), collections.abc.Iterable)) # True Но есть один момент, это функция iter(). Именно эту функцией использует например цикл for для получения итератора. Функция iter() в первую очередь для получения итератора из объекта, вызывает его метод __iter__. Если метод не реализован, то она проверяет наличие метода __getitem__ и если он реализован, то на его основе создается итератор. __getitem__ должен принимать индекс с нуля. Если не реализован ни один из этих методов, тогда будет вызвано исключение TypeError. from string import ascii_letters class SomeIterable3: def __getitem__(self, key): return ascii_letters[key] for item in SomeIterable3(): print(item)
40
Что такое итератор
Итератор (iterator) – это объект, который представляет поток данных. Повторяемый вызов метода __next__() (next() в Python 2) итератора или передача его встроенной функции next() возвращает последующие элементы потока. Если больше не осталось данных, выбрасывается исключение StopIteration. После этого итератор исчерпан и любые последующие вызовы его метода __next__() снова генерируют исключение StopIteration. Итераторы обязаны иметь метод __iter__, который возвращает сам объект итератора, так что любой итератор также является итерабельным объектом и может быть использован почти везде, где принимаются итерабельные объекты. Подробнее: Итераторы представлены абстрактным классом collections.abc.Iterator: class Iterator(Iterable): __slots__ = () @abstractmethod def __next__(self): 'Return the next item from the iterator. When exhausted, raise StopIteration' raise StopIteration def __iter__(self): return self @classmethod def __subclasshook__(cls, C): if cls is Iterator: return _check_methods(C, '__iter__', '__next__') return NotImplemented __next__ Возвращает следующий доступный элемент и вызывает исключение StopIteration, когда элементов не осталось. __iter__ Возвращает self. Это позволяет использовать итератор там, где ожидается итерируемых объект, например for. __subclasshook__ Проверяет наличие у класса метода __iter__ и __next__
41
Что такое генератор
В зависимости от контекста, может означать либо функцию-генератор, либо итератор генератора (чаще всего, последнее). Методы __iter__ и __next__ у генераторов создаются автоматически. С точки зрения реализации, генератор в Python — это языковая конструкция, которую можно реализовать двумя способами: как функция с ключевым словом yield или как генераторное выражение. В результате вызова функции или вычисления выражения, получаем объект-генератор типа types.GeneratorType. Канонический пример - генератор, порождающий последовательность чисел Фибоначчи, которая, будучи бесконечна, не смогла бы поместиться ни в одну коллекцию. Иногда термин применяется для самой генераторной функции, а не только объекта, возвращенного ей в качестве результата. Так как в объекте-генераторе определены методы __next__ и __iter__, то есть реализован протокол итератора, с этой точки зрения, в Python любой генератор является итератором. Когда выполнение функции-генераторы завершается (при помощи ключевого слова return или достижения конца функции), возникает исключение StopIteration.
42
Что такое генераторная функция
Генераторная функция - функция, в теле которой встречается ключевое слово yield. Будучи вызвана, такая функция возвращает объект-генератор (generator object) (итератор генератора (generator iterator)).
43
Что делает yield
yield замораживает состояние функции-генератора и возвращает текущее значение. После следующего вызова __next__() функция-генератор продолжает своё выполнение с того места, где она была приостановлена.
44
В чем отличие [x for x in y] от (x for x in y)
Первое выражение возвращает список (списковое включение), второе – генератор.
45
Что особенного в генераторе
Генератор хранит в памяти не все элементы, а только внутреннее состояние для вычисления очередного элемента. На каждом шаге можно вычислить только следующий элемент, но не предыдущий. Пройти генератор в цикле можно только один раз.
46
Как объявить генератор
использовать синтаксис (x for x in seq) оператор yield в теле функции вместо return встроенная функция iter, которая вызывает у объекта метод __iter__(). Этот метод должен возвращать генератор.
47
Как получить из генератора список
Передать его в конструктор списка: list(x for x in some_seq). Важно, что после этого по генератору уже нельзя будет итерироваться.
48
Что такое подгенератор
В Python 3 существуют так называемые подгенераторы (subgenerators). Если в функции-генераторе встречается пара ключевых слов yield from, после которых следует объект-генератор, то данный генератор делегирует доступ к подгенератору, пока он не завершится (не закончатся его значения), после чего продолжает своё исполнение. На самом деле yield является выражением. Оно может принимать значения, которые отправляются в генератор. Если в генератор не отправляются значения, результат данного выражения равен None. yield from также является выражением. Его результатом является то значение, которое подгенератор возвращает в исключении StopIteration (для этого значение возвращается при помощи ключевого слова return).
49
Какие методы есть у генераторов
__next__() – начинает или продолжает исполнение функции-генератора. Результат текущего yield-выражения будет равен None. Выполнение затем продолжается до следующего yield-выражения, которое передаёт значение туда, где был вызван __next__. Если генератор завершается без возврата значения при помощи yield, возникает исключение StopIteration. Метод обычно вызывается неявно, то есть циклом for или встроенной функцией next(). send(value) – продолжает выполнение и отправляет значение в функцию-генератор. Аргумент value становится значением текущего yield-выражения. Метод send() возвращает следующее значение, возвращённое генератором, или выбрасывает исключение StopIteration, если генератор завершается без возврата значения. Если send() используется для запуска генератора, то единственным допустимым значением является None, так как ещё не было выполнено ни одно yield-выражение, которому можно присвоить это значение. throw(type[, value[, traceback]]) – выбрасывает исключение типа type в месте, где был приостановлен генератор, и возвращает следующее значение генератора (или выбрасывает StopIteration). Если генератор не обрабатывает данное исключение (или выбрасывает другое исключение), то оно выбрасывается в месте вызова. close() – выбрасывает исключение GeneratorExit в месте, где был приостановлен генератор. Если генератор выбрасывает StopIteration (путём нормального завершения или по причине того, что он уже закрыт) или GeneratorExit (путём отсутствия обработки данного исключения), close просто возвращается к месту вызова. Если же генератор возвращает очередное значение, выбрасывается исключение RuntimeError. Метод close() ничего не делает, если генератор уже завершён.
50
Можно ли извлечь элемент генератора по индексу
Нет, будет ошибка. Генератор не поддерживает метод __getitem__.
51
Что возвращает итерация по словарю
Ключ. Порядок следования ключей не гарантируется (в 3.6 гарантируется неофициально, в 3.7 гарантируется). Для маленьких словарей порядок будет тот же, что и в объявлении. Для больших порядок зависит от расположения элементов в памяти. Особый класс OrderedDict учитывает порядок добавления ключей. for key in {'foo': 1, 'bar': 2}: process_key(key)
52
Как итерировать словарь по парам ключ-значение
Метод словаря .items() возвращает генератор кортежей (key, value).
53
Что такое сопрограмма
Сопрограмма (англ. coroutine) — компонент программы, обобщающий понятие подпрограммы, который дополнительно поддерживает множество входных точек (а не одну, как подпрограмма) и остановку и продолжение выполнения с сохранением определённого положения. Расширенные возможности генераторов в Python (выражения yield и yield from, отправка значений в генераторы) используются для реализации сопрограмм. Сопрограммы полезны для реализации асинхронных неблокирующих операций и кооперативной многозадачности в одном потоке без использования функций обратного вызова (callback-функций) и написания асинхронного кода в синхронном стиле. Python 3.5 включает в себе поддержку сопрограмм на уровне языка. Для этого используются ключевые слова async и await.
54