Python Flashcards

(13 cards)

1
Q

Что это простыми словами

A

Язык программирования с большим количеством написанных библиотек под разные задачи – на Python (пайтон) можно писать игры, вебсайты, модели машинного обучения, загрузку хранилища данных.

Он популярен у дата инженеров, потому что задачи для оркестратора Airflow пишут на Python, и у инструмента обработки данных Spark есть интерфейс PySpark.

Также он используется, потому что на нём удобно писать большие модульные системы. Он не самый быстрый с точки зрения производительности, но является стандартом в индустрии – поэтому проще обучить нового сотрудника или найти замену.

Также в DE популярны Java и Scala, редко – Rust или Go:
* Java из-за того что хадуп написан на джаве и раньше под него писали более “низкоуровневый” код. Ближе к тому что работает под капотом – меньше “переводчиков” нужно использовать
* Scala это язык, на котором можно писать “нативно” под Spark, аналогично джаве и хадупу
* Rust или Go используются для написания производительных приложений, например в высокочастотной торговле, когда важна каждая микросекунда

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

Итератор vs генератор

A

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

class MyNumbers:
  def \_\_iter\_\_(self):
    self.a = 1
    return self

  def \_\_next\_\_(self):
     x = self.a
    self.a += 1
    return x

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

def csv_reader(file_name):
    for row in open(file_name, "r"):
        yield row

for row in csv_reader(file_name):
    print(row)

Также генератор можно задать как generator expression (генераторное выражение).

csv_gen = (row for row in open(file_name))

for row in csv_gen:
    print(row)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Основные коллекции: dict, list, set, tuple +Изменяемые vs неизменяемые типы

A

dict, словарь – пары ключ-значение, совместим с JSON
db_conf = {“db”: “shop”, “schema”: “sales”, “table”: “orders”, “host”: “my.dbname.private.domain.com”, “port”: 5432}

Значениями могут быть число, строка, список, другой словарь, в принципе почти любой объект. Про ключи смотри “Что можно и нельзя использовать как ключ словаря”.
Поиск в словаре по ключу занимает O(1), т.к. нужно посчитать за константу хэш от ключа и обратиться напрямую по нему. В случае коллизии (когда хэш от двух разных ключей совпадает) занимает O(n).
Пробежаться по ключам словаря можно через
for key in d:
Также через in можно проверить, входит ли ключ в словарь.
Пробежаться сразу по ключам и значениям:
for key, val in d.items():

Получить значение словаря: d[“key”], добавить или перезаписать значение: d[“key”] = “value”.
Если значения нет, выдаст KeyError. Безопасно можно обратиться через **d.get(“key”, <default_return_value>)**. Без указания второго аргумента вернёт **None**, если ключа нет.</default_return_value>

Set это множество, во многом ведёт себя как словарь без значений. Есть только хэшируемые ключи, которые запоминают порядок вставки, но зато через O(1) можно проверить вхождение в сет. Сет хранит только уникальные значения (например, можно проверить, сколько уникальных значений в строке/списке, если преобразовать их в сет). Есть много уникальных методов из реляционной алгебры (union, intersect, except и пр.).
unique_labels = {‘low’, ‘mid’, ‘high’}

Список, list – одномерный массив значений, не обязательно одного типа, но обычно одного. Значениями может быть любой объект. Значения не отсортированы, но хранятся в порядке добавления.
ports = [5432, 8080, 80, 5050]

Добавить значение в конец:** l.append(value).
Извлекаются или переназначаются значения по индексу, то есть порядковому номеру, который начинается с нуля и идёт до n-1: ports[1] = 8080
Также можно обращаться к значениям по индексам “с правого конца”, от -1 до -
n**.
Частая ошибка во время исполнения кода IndexError – обратиться по индексу, который не существует в списке.

Пробежаться сразу по индексу и значению:
for i, elem in enumerate(l):
Не используй подход ниже, это не pythonic way

for i in range(len(l)):
    print(l[i])

Лучше обращайся сразу по for elem in l: или через enumerate.

Мы не узнаем, есть ли искомое значение в массиве, пока не проверим все по порядку, поэтому поиск в списке занимает O(n).

Сортировка занимает в среднем O(n*logn).

Tuple (тапл), или кортеж, это неизменяемый объект, который хранит значения разных типов. Может использоваться для возвращения строки из БД или передачи разнородных данных в одном объекте. Хранит порядок.
row = (‘Alice’, ‘Smith’, 25, ‘2B’, [‘Ru’, ‘Ge’], False)
Для создания кортежа с одним элементом добавь запятую в конец: my_tuple = (‘elem’,)

Обращаться к элементам нужно по индексам, как в списках.

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

Try except else finally

A

Используется для обработки ошибок. При написании кода важно предусматривать места, где что-то может пойти не так, и обрабатывать такие ошибки явно. Например, когда мы ожидаем ограниченный набор значений на входе, или проверяем что конфиг должен быть заполнен, или что в случае тайм-аута нужно попробовать отправить запрос ещё раз.

Аналог try-catch из других языков программирования.

  • try: попробовать выполнить участок кода
  • **except <Exception>**: если ошибка, выполни этот блок кода</Exception>
  • else: если исключение не вызвано, выполни код
  • finally: вне зависимости от того, вызвано исключение или нет, выполни код
d = {"key": "value"}
try:
    print(d["another_key"])
except KeyError:
    print(f"there’s no 'another_key' in d keys: {d.keys()}")
except Exception as e:
    raise e("some text I want to print")
else:
    print("do something here")
finally:
    print("почти никто не использует else и finally")

Можно вручную вызвать исключение любого типа, например:

raise TypeError("Only integers are allowed")

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

+Что можно и нельзя использовать как ключ словаря

A

Можно всё, что является неизменяемым и у чего реализован магический метод __hash__(), т.е. что при передаче в некоторую хэш-функцию вернёт хэш детерминированным образом. Важно, чтобы при обращении к словарю через ключ можно было посчитать хэш от ключа и найти связанное с ним значение (value) или точно знать, что такого ключа в словаре нет.

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

Context manager (with)

A

Используется при работе с контекстами – соединениями к БД или API, с файлами на диске или в объектном хранилище.
Очень важное свойство – вне зависимости от причины выхода из контекста (нормальное завершение работы или ошибка) закрывается коннект. Может пригодиться для работы с соединениями с какими-то внутренними сервисами (обычно уже написан и используется в хуке/операторе airflow и методах работы с Х в пакетах).

Выглядит так:
with open("test.txt") as f:  
    data = f.read()

with MongoDBConnectionManager('localhost', '27017') as mongo:
    collection = mongo.connection.SampleDb.test

Для написания своего контекстного менеджера определи магические методы _enter и exit
Сами детали с этими исключениями exc_type, exc_value обычно не спрашивают.

class ContextManager():
    def \_\_init\_\_(self):
        print('init method called')

    def \_\_enter\_\_(self):
        print('enter method called')
        return self
     
    def \_\_exit\_\_(self, exc_type, exc_value, exc_traceback):
        print('exit method called')

with ContextManager() as manager:
    print('with statement block')
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Распаковка списков и словарей *args kwargs

A

Некоторые функции могут работать с неограниченным количеством аргументов. Или мы хотим передать все, но использовать только некоторые из них. Тогда можно “распаковать” список или словарь через * и соответственно. Можешь встретить в операторах airflow, например.

*args представляет позиционные аргументы вида “значение”
kwargs – именованные аргументы вида “ключ-значение”

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

Слайсы в списках и строках

A

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

l = [0,1,2,3,4]
l[1:]    #[1,2,3,4]
l[:1]    #[0]
l[1:3]   #[1, 2]
l[1:5:2] #[1, 3]
l[1::2]  #[1, 3]
l[1::-2] #[1]
l[::-1]  #[4, 3, 2, 1, 0]
l[-1:]   #[4]
l[-2:]   #[3, 4]
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Type hints и doc-strings

A

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

При наведении на название функции IDE подтягивает это описание и предлагает автодополнение

Выглядят примерно так:

def get_columns(table:str, schema:str) -> list[str]:
    	""" Returns list of "column datatype" definitions. ""”

Также можно указывать тип для переменной или класса:

db_name: str
db_name = get_db_name(context)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

List and dict comprehensions

A

Короткая запись цикла “for” для создания и/или обработки списков и словарей: фильтрация, какая-то трансформация.

[elem for elem in list]
сolumns_to_ignore = [“col1”, “col2”]
quoted_columns = [“‘“ + col + “’” for col in columns if col not in columns_to_ignore]

{key: val for key, val in d.values()}
Устойчивая конструкция – X for X in object, и только в самом левом месте можно какие-то трансформации над этим Х проводить (ну или фильтровать после object).

Вложенный вариант пишется так:
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

длинный вариант
odd_numbers = []
for row in matrix:
    for element in row:
        if element % 2 != 0:
            odd_numbers.append(element)

 # короткий вариант
odd_numbers = [
    element for row in matrix for element in row if element % 2 != 0]
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

GIL + Async await, асинхронка, сборщик мусора и все связанное

A

Чаще всего здесь тебя ждут рассказ про сборщик мусора на Python, принцип его устройства, про библиотеку Asyncio и Global Interpreter Lock.

Сборщик мусора на Python реализован через счётчик ссылок. Python считает количество ссылок на объект, и как только все ссылки на объект заканчиваются — программа вычищает объект из памяти. Это необходимо, чтобы удалять из памяти ненужные объекты и оптимизировать использование ресурсов. Помимо подсчёта ссылок, Python также использует механизм выявления циклических ссылок, которые не могут быть удалены стандартным подсчётом. Корректную работу такого сборщика мусора обеспечивает GIL.

GIL — Global Interpreter Lock, особенность языка Python, запрещающая выполнение нескольких потоков параллельно в рамках одного интерпретатора. Это ограничение необходимо для обеспечения безопасности работы с памятью в многопоточных программах. Для обхода GIL можно использовать модуль multiprocessing, который создаёт отдельные процессы и позволяет задействовать несколько ядер процессора одновременно(для ускорения тяжёлых расчётов, например), но это не является полноценным решением проблемы отсутствия многопоточности.

Asyncio — модуль в Python, предназначенная для написания асинхронного кода. Она позволяет выполнять несколько задач одновременно, не блокируя выполнение программы, используя кооперативную многозадачность.

Основные компоненты asyncio включают в себя:

Event Loop (Цикл событий) — основной механизм, который управляет выполнением асинхронных задач.

Coroutines (Корутины) — специальные функции, которые могут приостанавливать своё выполнение с помощью ключевых слов async и await.

Tasks (Задачи) — обёртка над корутинами, позволяющая планировать их выполнение в цикле событий.

Futures (Будущие объекты) — представляют собой результат асинхронной операции, который станет доступным в будущем.

Можно указать на сомнительную производительность и высокую сложность Asyncio и сказать, что лучше пользоваться другими DE-инструментами, а AsyncIo использовать только в случае если необходимо работать с каким-нибудь Web API и для этого нет более подходящих инструментов, чем обычно занимается Back-end разраб.

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

ООП, основные принципы

A

Объектно-Ориентированное Программирование - важно рассказать про 4 основных принципа (иногда три, исключают абстракцию, не холиварьте по этому поводу на собесе)

Инкапсуляция

Скрытие внутренней реализации объекта и предоставление доступа к данным только через методы.

Обеспечивает защиту данных и контроль над их изменением.

Де-факто в Python не существует способа сделать метод или объект приватным, но в среде разработчиков принято такие методы всё равно обозначать двумя подчёркиваниями с каждой стороны ( __method__ ) и не менять их вне класса, чтобы соблюдать инкапсуляцию

Наследование

Механизм, позволяющий одному классу (потомку) использовать и расширять функциональность другого класса (родителя).

Обеспечивает повторное использование кода и создание иерархии классов

Тут важно упомянуть про функцию .super() - которая позволяет обращаться к родительскому классу

Полиморфизм - это про взаимодействие функции или класса с разными типами данных и в разных условиях +- одинаково. Например функция len() может возвращать длину строки, или кол-во элементов в списке, получая на вход разные форматы данных(строку, список или ещё что-нибудь). В случае с классами, приведу пример

class Cat:
        def make_sound(self):
            print("Meow")
class Dog:
        def make_sound(self):
            print("Bark")

cat1 = Cat()
dog1 = Dog()

for animal in (cat1, dog1):
    animal.make_sound()

При выполнении код выведет: 
Meow
Bark

Таким образом мы использовали два разных метода с одним и тем же названием на разных классах и получили логически схожий результат, ожидаемый. Это пример полиморфизма классов

Абстракция

Это когда мы переходим от конкретных свойств каждого класса к общим, которые объединяют несколько классов
Пример:
Есть Data Vault модель, таблицы которой описаны классами в Python. В ней есть три сущности Hub, Link, Satellite. У них есть названия - Seller_Hub, Sell_Receipt_Link, Seller_Satellite и какие-то другие общие характеристики, например наличие бизнес-ключа. Когда мы понимаем, что у всех классов есть общие характеристики логическая связь, мы можем создать новый класс-абстракцию, Entity, которая в себе будет содержать все общие характеристики сущностей модели Data Vault(В нашем случае - название и бизнес-ключ). Такой же абстракцией может быть сущность Hub над классами Seller_Hub, Buyer_Hub, Car_Hub

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

immutable vs mutable

A

Как вариант можно перечислить: set, dict, list - изменяемые
bool, float, int, str, frozenset, tuple - неизменяемые

Будет круто добавить, что изменяемые всегда лежат в одной и той же ячейке памяти(id() от объекта вернёт всегда один результат, а НЕизменяемые - нет.
Неизменяемые не всегда на 100% не меняются, например:

t = ('holberton', [1, 2, 3])
t[1].append(4)
print(t)

Выведет: (‘holberton’, [1, 2, 3, 4])
Cписок внутри кортежа изменился - потому что он изменяемый. Но сам кортеж продолжает ссылаться на тот же список и таким образом он “не изменился”

Также можно добавить, что неизменяемые объекты могут быть ключом в словаре, а изменяемые нет и на неизменяемые можно вешать магический метод __hash__ и по ним быстрее идёт поиск

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