Python Flashcards

(26 cards)

1
Q

Типы данных

A

dict, словарь – пары ключ-значение
Ключом могут быть неизменяемые объекты, у которых реализован магический метод __hash__()
Значениями могут быть любые объекты
Типичные операции:
d[key], get(), keys(), values(), items().

Set это множество, во многом ведёт себя как словарь без значений.
Это изменяемый набор уникальных и неупорядоченных элементов.
Нет порядка. Нет повторов.
Хранит:
Только неизменяемые объекты: int, float, string, tuple (tifs)
Можно вложить tuple (если его элементы хэшируемые).
Типичные операции:
add(), remove(), union(), intersection().

Список, list – это упорядоченная коллекция объектов. Списки могут иметь сколько угодно уровней вложенности и хранить неограниченное количество объектов. Кроме того, в одном списке могут одновременно храниться объекты разных типов.
Типичные операции:
append(), remove(), pop(), срезы lst[1:3].

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

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

Чем отличается генератор от итератора. Что экономит память, а что быстрее?

A

Итератор это класс, в котором реализованы два магических методы __iter__() и __next__()
__iter__() — возвращает сам объект (итератор),
__next__() — возвращает следующий элемент.

По объектам этого класса можно будет пробегаться циклом или запрашивать next(iter(myObject)).
Вычисляет и хранит всю последовательность в памяти, ей ограничен.

Можно создать из любой итерируемой коллекции (список, кортеж, строки, словари).
После окончания итерации объект становится “пустым” — нельзя пройтись по нему второй раз без пересоздания.

Генератор это функция, использующая ключевое слово yield
Также генератор можно задать как generator expression (генераторное выражение).
Хранит только текущее значение и то, как вычислить следующее. Может работать с бесконечно большими последовательностями.

Где его используют?
Обработка больших файлов
Чтение построчно без загрузки всего файла в память
Потоковая обработка данных (data pipeline)

Что экономит память — итератор или генератор?
Оба экономят память, потому что не хранят все элементы в памяти сразу, а отдают по одному.
Но генератор чаще используется именно для экономии памяти, так как его проще и быстрее написать. Он идеален для потоковой обработки данных, например, построчного чтения файлов или обработки больших объёмов.

Что быстрее — итератор или генератор?
В среднем генератор немного быстрее, потому что его реализация компактнее и оптимизирована Python’ом под капотом.
Ручной итератор через класс даёт больше гибкости, но из-за лишнего кода и вызовов может быть чуть медленнее.
На практике разница в скорости минимальна, но генераторы выигрывают по удобству и читаемости.

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

У нас есть объект итератор. Мы закинули его в for цикл
Как работает инициализация оператора и как он понимает когда остановиться?

A

Вызывается iter(obj).
Вызывает метод __iter__() объекта.
Он должен вернуть итератор — объект, у которого есть метод __next__()
Каждый вызов __next__() возвращает следующий элемент.
Если элементы закончились - выбрасывается исключение StopIteration
for-цикл это ловит и завершается без ошибки

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

В чем отличие кортежа от списка?

A

Cписки являются изменяемыми, а кортежи неизменяемыми.

Изменяемые объекты всегда лежат в одной и той же ячейке памяти(id() от объекта вернёт всегда один результат, а неизменяемые - нет.
При попытке изменить неизменяемый объект, в памяти просто создастся новый объект, который будет уже с другим id() (адресом в памяти) и своим hash() (если он хешируемый) и переменная начинает ссылаться на этот новый объект

Список и кортеж реализованы как лист из указателей (ссылок) на Python объекты, т.е. физически они не хранят объекты рядом с друг другом. Когда вы удаляете объект из списка происходит удаление ссылки на этот объект. Если на объект ещё кто-то ссылается, то он продолжит находиться в памяти.

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

Как реализованы словари и множества в Python?

A

Типы dict и set в Python построены на основе хэш-таблиц.

Что происходит когда ключ добавляется? Когда значение добавляет значение в set?
Вызывается hash(key) — получаем хеш объекта.
По хешу вычисляется индекс в хеш-таблице.
Если слот свободен — кладём туда пару (dict) или ключ (set).
Если занят — Python проверяет == и, при необходимости, ищет следующую подходящую ячейку (разрешение коллизий). Если == это True - значение обновляется

Почему нельзя использовать изменяемые типы данных в качестве ключей?
Потому что изменяемые объекты могут менять свой хеш во времени.
Если ключ после вставки в dict изменится, мы потеряем доступ к значению.
Поэтому такие типы, как list, dict, set, не имеют __hash__() и не могут быть ключами.

Почему tuple можно использовать как ключ, а list — нет, хотя они похожи?
tuple неизменяемый, а значит безопасен как ключ.
tuple реализует __hash__(), если все его элементы тоже хешируемые.
list — изменяемый и не реализует __hash__(), чтобы предотвратить логические ошибки при использовании в хеш-таблицах.

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

Зачем нужен hash если можно сравнить посимвольно?

A

hash нужен для быстрого сравнения объектов. Это нужно например для словарей, именно за счёт хэша доступ по ключу в словарях так быстро работает. Полное сравнение объектов может работать долго, поэтому сначала сравниваются хэши объектов и только при их совпадении производится полное сравнение через eq.

В идеале хэш должен быть с одной стороны небольшой по размеру, а с другой стороны разный для разных объектов. Это не всегда возможно и оправданно, поэтому в реальности хэш это некий компромисс и коллизии всё же бывают, но их немного, поэтому поиск по ключу в словаре и проверка наличия элемента в set работает моментально (за время порядка O(1)).

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

Хеш всегда уникален?
Есть два объекта с одинаковыми хешами. Это значит что объекты равны?

A

Нет. Хеш-функция может возвращать одинаковые значения для разных объектов — это называется коллизия.
Поэтому Python дополнительно сравнивает объекты через ==.

Разные хеши означает, что объекты точно разные.
Python даже не вызывает ==, если хеши отличаются.

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

Что происходит при коллизии хешей в словаре?

A

В Python есть механизм, который заключается в том что
если мы получаем такой же хеш, как у уже существующего ключа,
то сначала проверяется __eq__ — равны ли сами объекты.
Если объекты разные — это коллизия. В этом случае Python ищет новое место
в хеш-таблице по стратегии открытой адресации (обычно квадратичный пробинг),
и размещает новый ключ в следующей подходящей ячейке.

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

линейный пробинг: i+1, i+2, i+3…
квадратичный пробинг (как в Python): i+1², i+2², i+3²…

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

Какие типы данных нельзя использовать в качестве ключей словаря?

A

Все изменяемые типы, у которых нет __hash__():
list, dict, set, tuple, если внутри есть хотя бы один не-хешируемый объект

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

Кортеж считается неизменяемым, потому что его структура — фиксированная:
количество элементов, порядок, тпозиции ссылок на объекты
Ты не можешь: добавить или удалить элемент, заменить один элемент на другой (t[0] = 10 — ошибка)
Но сам кортеж содержит ссылки на объекты, а не значения.
Если внутри лежит изменяемый объект (например, список), то его содержимое можно менять — но ссылка остаётся той же, а значит структура кортежа формально не нарушена.

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

Как хранятся ссылки на объекты внутри кортежа?

A

Кортеж хранит массив ссылок на объекты, как указатели:
не значения напрямую, а именно адреса в памяти,
это означает, что кортеж сам по себе не владеет содержимым — он просто ссылается.

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

Как сравниваются объекты (сравниваем объект1 и объект2)? Как происходит сравнение? Как можно понять что они одинаковые? Через какой метод они сравниваются?

A

Методом __eq__

Когда ты сравниваешь два объекта в Python, например:
if obj1 == obj2:
происходит вызов магического метода __eq__().

Механизм сравнения:
obj1 == obj2 → Python вызывает obj1.__eq__(obj2)
Если __eq__() не переопределён в классе — используется реализация по умолчанию (сравнение по id, то есть по адресу в памяти).
Если __eq__() возвращает True, значит объекты считаются равными.

Логика такая что у каждого класса можно реализовать метод eq который будет сравнивать экземпляры двух классов. Если __eq__ не реализовать — Python сравнивает объекты по адресу в памяти (то есть obj1 is obj2).

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

В переменную a кладем словарь. Хочу, чтобы в переменной b лежал такой же словарь, как и в переменной a

A

Через метод copy.copy()

Есть еще метод copy.deepcopy()

Разница:
copy.copy() — поверхностная (shallow) копия
Копирует только сам объект, но не копирует вложенные объекты (они останутся ссылками на те же объекты).
copy.deepcopy() — глубокая копия
Копирует всё: и сам объект, и все вложенные объекты (рекурсивно).

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

Пользуешься типизацией? mypy
Типизируешь методы? Что они возвращают

A

Пользуюсь типизацией точечно
Airflow-операторах и Spark-джобах тоже использую типизацию для __init__, execute()
Явно указать типы аргументов (например, int, str, dict[str, Any])
Сделать код читаемее и понятнее для IDE и mypy
Защититься от случайных ошибок
source_path: str
Это путь к исходным данным, например в HDFS или S3.
batch_size: int
Размер порции данных, которые обрабатываются за один шаг.

Типизирую ключевые аргументы и возвращаемые значения: пути (str), порции данных (int), списки значений (List[str]), Spark-таблицы (DataFrame) и контекст выполнения Airflow (Context)

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

Что такое декоратор и зачем он нужен? Писала декораторы?Как передавать параметры в декоратор? Приводил ли примеры написания декораторов?

A

Работала с декораторами
Декоратор — это функция, которая оборачивает другую функцию или метод, чтобы добавить к ней дополнительную логику без изменения её кода.
Используется, чтобы переиспользовать функциональность

Не писала сама, не было такой необходимости
Но знаю как реализовать.
— Мы пишем функцию, которая принимает другую функцию как аргумент
— Внутри неё определяем вложенную функцию, которая оборачивает поведение оригинальной
— В конце возвращаем эту вложенную функцию
— И используем синтаксис @имя_декоратора над функцией, которую хотим обернуть

Можно передавать параметры в декоратор?
Да, в декоратор можно передавать параметры, но тогда он становится функцией, которая возвращает декоратор — то есть ещё один уровень обёртки.

Как устроена реализация декоратора с параметрами?
1. Внешняя функция принимает параметры декоратора.
2. Она возвращает сам декоратор, который принимает целевую функцию.
3. Внутри декоратора — обёртка, которая добавляет нужную логику и вызывает оригинальную функцию.

17
Q

Как переопределить метод? Нужен ли там декоратор?

A

Переопределение метода (override) — это когда в подклассе ты пишешь свою версию метода, который уже есть в родительском классе.
Это делается без декоратора, просто через объявление метода с тем же именем.
Декоратор не обязателен, но в новых версиях Python можно использовать @override из typing для явного указания IDE и проверки через mypy.

18
Q

Пользовалась ли ООП python?

A

Я использовала ООП, когда это было нужно — например, при написании кастомных Airflow операторов. Там я наследовалась от базового класса BaseOperator или SparkSubmitOperator, переопределяла метод execute, задавала параметры через __init__ — классический ООП-подход.

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

19
Q

Как инициализировать класс? Как будет выглядеть конструкция?

A

Чтобы создать (инициализировать) экземпляр класса в Python, нужно:
Определить конструктор в самом классе через метод __init__, указав там необходимые параметры.
Вызвать класс как функцию, передав аргументы в скобках — это и создаст новый объект.

Есть метод super и мы перенимаем все что есть в родительском классе
Можем поменять методы в родительском классе, можем задать новые или переопределить
Будут использоваться методы которые мы предопределили

20
Q

Использовала магические методы (функции)? Что такое магические методы?

A

Магические методы - это специальные методы в Python, которые начинаются и заканчиваются двумя подчёркиваниями (dunder-методы), например __init__, __str__, __eq__. Они позволяют переопределить поведение встроенных операций языка.
__init__ вызывается при создании объекта. используется для инциализации. это метод конструктора. мы в инит сдаем базово переменные все, инициализируем класс
__new__ это когда нужно управлять процессом создания
__eq__ — отвечает за сравнение ==
__hash__ отвечает за вычисление хеш-значения объекта (например, для ключей в словарях и множеств).
Методов много

__getattribute__(self, name)
Вызывается при любом доступе к атрибуту (obj.attr).
Очень мощный и опасный метод — легко сделать бесконечную рекурсию.
__setattr__(self, name, value)
Вызывается при установке значения атрибуту (obj.attr = value).
Полезен, если хочешь контролировать или валидировать установку атрибутов.
С магическими методами я сталкивалась в основном при изучении языка и написании классов, но в продакшене использовала их нечасто — в проектах с данными чаще важна читаемая логика, чем переопределение поведения объектов.
Но при этом я понимаю, как и зачем они используются.
Если бы пришлось разрабатывать полноценные библиотеки, я бы активно это использовала.

21
Q

Чем отличаются @staticmethod, @classmethod и @property?

A

Это встроенные (built-in) декораторы Python, которые изменяют поведение методов класса.

staticmethod это декоратор, который связан с классом, а не с его экземплярами. Чтобы быть вызванным, он не требует создания экземпляра класса и не имеет доступа к экземпляру.
Он не может модифицировать состояние класса или его экземпляров.
Это просто функция, логически связанная с классом.

classmethod это декоратор, который превращает обычный метод в метод класса.
Он привязан к самому классу, а не к экземпляру.
Первым аргументом такого метода всегда идёт класс (cls), а не self.
С помощью @classmethod можно обращаться к атрибутам класса и создавать новые экземпляры.

property превращает метод в атрибут.
Позволяет добавить логику при обращении к полям объекта — например, при чтении, записи или удалении.
Может использоваться с @getter, @setter и @deleter для управления чтением, записью и удалением значений. Подходит для инкапсуляции и валидации.

@getter, @setter и @deleter — это части свойства @property, которые позволяют управлять логикой чтения, записи и удаления атрибута, сохраняя доступ как к обычному полю.
Это удобно для инкапсуляции, валидации и скрытия внутренней реализации.

22
Q

Private? Protected методы как-то реализованы? А сама пользуешься этим? Есть ли в питоне приватные функции?

A

Приватные функции не реализованы. Просто принято, что есть:

Protected методы (защищённые):
Обозначаются через один подчёркивание: _method_name
Служит для обращения внутри класса и во всех его дочерних классах

Private методы (приватные):
Обозначаются двумя подчёркиваниями: __method_name
Служит для обращения только внутри класса
Name mangling — это автоматическое переименование имён с двумя подчёркиваниями (), чтобы их нельзя было случайно переопределить или вызвать извне.

В питоне нет строгой приватности, но я использую _ для внутренних методов (например, _fetch_data, _validate_input) — чтобы отделить служебную логику.
__ применяю редко, когда важно избежать конфликтов имён. В основном придерживаюсь соглашений, особенно в больших пайплайнах и классах.
_fetch_data, _validate_input как внутренние вспомогательные методы в кастомных классах (например, в Airflow-операторах). Это помогает структурировать код и избегать лишнего доступа извне.
_log, _context — стандартные protected-поля Airflow, которые активно использовала для логирования и получения параметров DAG.

23
Q

Что такое MRO (Method Resolution Order)?

A

MRO (Method Resolution Order) — это порядок, в котором Python ищет методы и атрибуты в случае наследования, особенно при множественном наследовании.
Он определяется через алгоритм С3 - линеаризации
Принципы C3-линеаризации:
Дочерний класс всегда идёт перед родителями
Если один и тот же класс упоминается несколько раз, используется только первое появление
Сохраняется порядок, указанный в class X(A, B, C)

Он гарантирует, что порядок будет устойчивым, без конфликтов, и super() будет работать предсказуемо.

24
Q

Проблема ромбовидного наследования (Diamond Problem)

A

Проблема ромбовидного наследования возникает, когда класс наследуется от двух (или более) предков, которые, в свою очередь, имеют общего предка. В результате получается «ромб»

Как Python решает эту проблему
Method Resolution Order (MRO)
Python вычисляет упорядоченный список классов, по которому будет искать методы.
Алгоритм C3-линеаризации
Он строит MRO так, чтобы:
все базовые классы шли после дочерних,
сохранялся порядок, в котором указаны родительские классы в объявлении,
ни один класс не появлялся в списке более одного раза.
super()
При использовании super() внутри методов Python следует этому же списку MRO, что позволяет корректно цеплять вызовы по «ромбовидной» иерархии без дублирования.

Итог: благодаря MRO и C3-линеаризации Python однозначно и предсказуемо определяет, чью версию метода использовать, и избегает двукратного вызова одного и того же класса при многократном наследовании.

25
Как реализовать контекстный менеджер? Для чего используются методы __enter__ и __exit__? Как реализовать?
Контекстный менеджер — это объект, который управляет входом и выходом из блока with. Он позволяет гарантированно освободить ресурсы, даже если внутри блока возникла ошибка. Контекстные менеджеры нужны для автоматического управления ресурсами (файлы, соединения, блокировки). Реализуются через __enter__ и __exit__ или через @contextmanager. __enter__() — вызывается при входе в with, можно вернуть ресурс. __exit__() — вызывается при выходе, даже если была ошибка. Принимает 3 аргумента: exc_type, exc_val, exc_tb — если в блоке with произошло исключение.
26
Что такое дескриптор?
Дескрипторы в Python — это объекты, которые реализуют один или несколько из специальных методов: __get__(), __set__() и __delete__(). Они позволяют управлять доступом к атрибутам класса и предоставляют механизм для кастомизации поведения при доступе к этим атрибутам. Дескрипторы используются в качестве атрибутов класса. Когда вы пытаетесь получить доступ к атрибуту, который является дескриптором, Python автоматически вызывает соответствующий специальный метод дескриптора. Дескрипторы могут быть полезны для различных целей, таких как: Валидация значений атрибутов Кеширование результатов вычислений Логирование доступа к атрибутам