Послідовністю в Python називається ітерований об'єкт, який підтримує ефективний доступ до елементів за допомогою цілочисельних індексів через спеціальний метод __getitem__() та підтримує метод __len__(), що повертає довжину послідовності.
До основних вбудованих типів послідовностей належать list, tuple, range, str та bytes.
Послідовності також опціонально можуть реалізовувати методи count(), index(), __contains__() та __reversed__() та інші.
x in s,x not in s– чи знаходиться елемент x у послідовності s (для рядків та послідовностей байтів – чи є x підрядком s)s + t– конкатенація послідовностей s та ts * n, n * s– конкатенація n нерекурсивних копій послідовності ss[i]– i-й елемент послідовності ss[:i]- зріз усіх елементів послідовності s до i (не включаючи i)s[i:]- зріз усіх елементів послідовності s від i до останнього елементаs[-i:]- зріз останніх i елементів послідовності ss[::-1]- перевернути послідовністьs[i:j]– зріз послідовності s від i до j (не включаючи j)s[i:j:k]– зріз послідовності s від i до j з кроком k (не включаючи j)len(s)– довжина послідовності smin(s)– мінімальний елемент послідовності smax(s)– максимальний елемент послідовності ss.index(x[, i[, j]])– індекс першого входження x (опціонально – починаючи з позиції i та до позиції j)s.count(x)– загальна кількість входжень x у ssum(s)– сума елементів послідовності
Незмінні послідовності зазвичай реалізують операцію hash(s) – хеш-значення об'єкта.
Більшість змінних послідовностей підтримують такі операції:
s[i] = x– елемент з індексом i замінюється на xs[i:j] = t,s[i:j:k] = t– елементи з індексами від i до j (з кроком k) замінюються вмістом ітерованого об'єкта tdel s[i:j],del s[i:j:k]– видалення відповідних елементів з послідовностіs.append(x)– додавання x в кінець послідовностіs.clear()– видалення всіх елементів послідовностіs.copy()– нерекурсивна копія послідовностіs.extend(t)– додавання всіх елементів ітерованого об'єкта в кінець послідовностіs.insert(i, x)– вставка елемента x за індексом is.pop(),s.pop(i)– повернення значення за індексом i (за замовчуванням – останній елемент) та видалення його з послідовностіs.remove(x)– видалення першого входження xs.reverse()– розворот послідовності у зворотньому порядку
Залежить від версії Python. У другій гілці два типи: однобайтні рядки та Юнікод представлені класами str та unicode відповідно. У третьому Python є один вид рядків str, який представляє собою Юнікод. Однобайтних рядків немає, замість них є тип bytes, тобто ланцюжок байтів.
Ні, рядки незмінні. Операції заміни, форматування та конкатенації повертають новий рядок.
Щоб об'єднати, потрібен метод рядка .join(). Щоб розбити, метод .split().
Кодувати – перевести Юнікод у байтовий рядок. Викликати метод .encode() у рядка.
Декодувати – відновити рядок із ланцюжка байтів. Викликати метод .decode() у об'єкта str або bytes (версії Python 2 та 3 відповідно).
В обох випадках явно передавати кодування, інакше буде використана та, що визначена в системі за замовчуванням. Бути готовим зловити виключення UnicodeEncodeError, UnicodeDecodeError.
Списки – це змінні послідовності, які зазвичай використовуються для зберігання однотипних даних (хоча 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]Діапазони – незмінні послідовності чисел, які задаються початком, кінцем та кроком. Представлені класом 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]a, b, c = (1, 2, 3)
Дві послідовності рівні, якщо вони мають однаковий тип, рівну довжину та відповідні елементи обох послідовностей рівні.
Послідовності однакових типів можна порівнювати. Порівняння відбуваються у лексикографічному порядку: послідовність меншої довжини менша, ніж послідовність більшої довжини, якщо ж їхні довжини рівні, то результат порівняння дорівнює результату порівняння перших елементів, що відрізняються.
Об'єкт називається хешованим, якщо він має хеш-значення (ціле число), яке ніколи не змінюється протягом його життєвого циклу та повертається методом __hash__(), та може порівнюватися з іншими об'єктами (реалізує метод __eq__()). Рівні хешовані об'єкти повинні мати рівні хеш-значення.
Всі стандартні незмінні об'єкти хешовані. Всі стандартні змінні об'єкти не хешовані.
Множина – це невпорядкована колекція хешованих об'єктів, які не повторюються. У множинах немає поняття позиції елемента. Відповідно, вони не підтримують індексацію та зрізи. Вбудовані класи множин: set (змінна множина), frozenset (незмінна множина).
Зазвичай використовуються для перевірки елемента на входження в множину та видалення повторень елементів і виконання таких операцій, як об'єднання, перетин, різниця та симетрична різниця.
set([iterable]),frozenset([iterable])– створення множини (порожньої або з елементів ітерованого об'єкта)len(s)– кількість елементів множиниx in s,x not in s– перевірка наявності елемента в множиніs.isdisjoint(t)– перевірка того, що дана множина не має спільних елементів із заданоюs.issubset(t),s <= t– перевірка того, що всі елементи множини s є елементами множини ts < t– перевірка того, що s <= t та s != ts.isuperset(t),s >= t– перевірка того, що всі елементи множини t є елементами множини ss > t– перевірка того, щоs >= tтаs != ts.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)– видалити елемент з множини; якщо такого елемента немає, виникає виключення KeyErrors.discard(element)– видалити елемент з множини, якщо він у ній знаходитьсяs.pop()– видалити з множини та повернути довільний елемент; якщо множина порожня, виникає виключення KeyErrors.clear()– видалити всі елементи множини
Перевірка множин на рівність відбувається поелементно, незалежно від типів множин.
Відображення (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.
Числові ключі в словниках підпорядковуються правилам порівняння чисел. Таким чином, int(1) та float(1.0) вважаються однаковим ключем. Однак через те, що значення типу float зберігаються наближено, не рекомендується використовувати їх як ключі.
>>> {True: 'yes', 1: 'no', 1.0: 'maybe'}
{True: 'maybe'}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() повертає ітератор.d.keys()– у Python 3 повертає об'єкт представлення словника, що відповідає ключам словника. У Python 2 повертає відповідний список, а метод iterkeys() повертає ітератор.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() повертає ітератор.
Об'єкти, що повертаються методами items(), keys() та values() – це об'єкти представлення словника. Вони надають динамічне представлення елементів словника, тобто зміни даного словника автоматично відображаються і на цих об'єктах.
Операції з представленнями словників:
iter(dictview)– отримання ітератора за ключами, значеннями або парами ключів та значень.len(dictview)– кількість елементів у словнику.x in dictview– перевірка існування ключа, значення або пари ключ-значення в словнику.
Метод списку .sort() і вбудована функція sorted() приймають параметр key. Ним має бути викликуваний об'єкт, який приймає черговий елемент (у нашому випадку словник) і повертає значення-критерій сортування.
Код нижче показує, як відсортувати список людей за віком:
users = [{'age': 30}, {'age': 20}, {'age': 10}]
users.sort(key=lambda user: user['age'])
>>> [{'age': 10}, {'age': 20}, {'age': 30}]Ключем словника може бути будь-який хешований незмінний об'єкт: число, рядок, datetime, функція і навіть модуль. Такі об'єкти мають метод __hash__(), який однозначно зіставляє об'єкт з деяким числом. За цим числом словник шукає значення для ключа.
Списки, словники та множини змінні та не мають методу хешування. При підстановці їх у словник виникне помилка.
Хеш кортежу обчислюється рекурсивно за всіма елементами. Так, кортеж (1, (True, (42, ('hello', )))) складається тільки з незмінних елементів, тому може бути ключем. Однак такий кортеж (1, (True, (42, ({'hello': 'world'}, )))) містить глибоко всередині словник, тому хеш не може бути розрахований.
keys = ['foo', 'bar', 'baz']
vals = [1, 2, 3]
dict(zip(keys, vals))
>>> {'baz': 3, 'foo': 1, 'bar': 2}Функція zip віддає список пар N-х елементів. Конструктор dict приймає список пар. Кожну пару він розглядає як ключ та значення відповідно.
Хеш-таблиця – це розріджений масив (масив, у якому є незаповнені позиції). Ячейки хеш-таблиці в dict містять два поля: посилання на ключ і посилання на значення елемента.
Python прагне залишати не менше третини ячейок порожніми; якщо хеш-таблиця стає надмірно заповненою, вона копіюється в нову ділянку пам'яті, де є місце для більшої кількості ячейок.
Для поміщення елемента в хеш-таблицю потрібно насамперед обчислити хеш-значення ключа елемента. Це робить вбудована функція hash().
Коли хеш-функція повертає однакову відповідь для різних даних.
Пошук буде швидшим у dict та set, тому що це хеш-таблиці, доступ до елемента яких виконується за O(1). Для list та tuple пошук буде виконуватися в середньому за O(n).
Виняток працює тільки для дуже маленьких списків довжиною до 5 елементів. У цьому випадку інтерпретатору буде швидше пробігтися по списку, ніж обчислити хеш.
Вирази *args та **kwargs оголошують у сигнатурі функції. Вони означають, що всередині функції будуть доступні змінні з іменами args та kwargs (без зірочок).
args – це кортеж, який накопичує позиційні аргументи. kwargs – словник іменованих аргументів, де ключ – ім'я параметра, значення – значення параметра.
Важливо: якщо у функцію не передано жодних параметрів, змінні будуть відповідно рівні порожньому кортежу та порожньому словнику, а не None.
Чому використовувати змінні об'єкти як параметри за замовчуванням погано. Наведіть приклад поганого випадку. Як виправити
Функція створюється одного разу при завантаженні модуля. Іменовані параметри та їх дефолтні значення теж створюються один раз та зберігаються в одному з полів об'єкта-функції.
У нашому прикладі 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]Сказане вище актуально в т.ч. для множин та словників.
Можна, функція у Python є об'єктом першого порядку: допускає присвоєння, передачу у функцію, видалення.
Можна. Така функція буде видима тільки всередині першої функції.
Це анонімні функції. Вони не резервують імені в просторі імен. Лямбди часто передають у функції map, reduce, filter.
Лямбди у Python можуть складатися тільки з одного виразу. Використовуючи синтаксис дужок, можна оформити тіло лямбди у кілька рядків.
Використовувати крапку з комою для розділення операторів не можна.
nope = lambda: passriser = lambda x: raise Exception(x)
Ні, при завантаженні модуля виникне виключення SyntaxError. У тілі лямбди може бути тільки вираз. pass та raise є операторами.
У таких мовах, як C++, є змінні, що зберігаються на стеку та у динамічній пам'яті. При виклику функції ми поміщаємо всі аргументи на стек, після чого передаємо управління функції.
У Python відмовились від такого механізму, замінювачем служить механізм зв'язування (assignment) імені змінної з об'єктом, наприклад при створенні змінної:
var = "john"
Інтерпретатор створює об'єкт «john» і «ім'я» var, а потім зв'язує об'єкт з даним ім'ям. При виклику функції нових об'єктів не створюється, замість цього у її просторі видимості створюється ім'я, яке зв'язується з існуючим об'єктом.
Синтаксично це виглядає як функція, що знаходиться повністю в тілі іншої функції. При цьому вкладена внутрішня функція містить посилання на локальні змінні зовнішньої функції. Кожного разу при виконанні зовнішньої функції відбувається створення нового екземпляра внутрішньої функції, з новими посиланнями на змінні зовнішньої.
Контейнер – це тип даних, який інкапсулює в собі значення інших типів. Списки, кортежі, множини, словники тощо є контейнерами.
Ітерований об'єкт (в оригінальній термінології – «iterable») – це об'єкт, який може повертати значення по одному за раз.
Приклади: всі контейнери та послідовності (списки, рядки тощо), файли, а також екземпляри будь-яких класів, у яких визначений метод __iter__() або __getitem__().
Ітеровані об'єкти можуть бути використані всередині циклу for, а також у багатьох інших випадках, коли очікується послідовність (функції sum(), zip(), map() тощо).
Ітератор (iterator) – це об'єкт, який представляє потік даних. Повторюваний виклик методу __next__() (next() у Python 2) ітератора або передача його вбудованій функції next() повертає подальші елементи потоку.
Якщо більше не залишилось даних, кидається виключення StopIteration. Після цього ітератор вичерпаний і будь-які подальші виклики його методу __next__() знову генерують виключення StopIteration.
Ітератори зобов'язані мати метод __iter__, який повертає сам об'єкт ітератора, тому будь-який ітератор також є ітерованим об'єктом.
Залежно від контексту, може означати або функцію-генератор, або ітератор генератора (частіше всього останнє).
Методи __iter__ та __next__ у генераторів створюються автоматично.
З точки зору реалізації, генератор у Python — це мовна конструкція, яку можна реалізувати двома способами: як функція з ключовим словом yield або як генераторний вираз.
Генераторна функція – функція, у тілі якої зустрічається ключове слово yield. Будучи викликаною, така функція повертає об'єкт-генератор.
yield заморожує стан функції-генератора та повертає поточне значення. Після наступного виклику __next__() функція-генератор продовжує своє виконання з того місця, де вона була призупинена.
Перший вираз повертає список (спискове включення), другий – генератор.
Генератор зберігає в пам'яті не всі елементи, а тільки внутрішній стан для обчислення чергового елемента. На кожному кроці можна обчислити тільки наступний елемент, але не попередній. Пройти генератор у циклі можна тільки один раз.
- використати синтаксис
(x for x in seq) - оператор
yieldу тілі функції замістьreturn - вбудована функція
iter, яка викликає у об'єкта метод__iter__(). Цей метод має повертати генератор.
Передати його в конструктор списку: list(x for x in some_seq). Важливо, що після цього по генератору вже не можна буде ітеруватися.
У Python 3 існують так звані підгенератори (subgenerators). Якщо у функції-генераторі зустрічається пара ключових слів yield from, після яких слідує об'єкт-генератор, то даний генератор делегує доступ до підгенератора, поки він не завершиться, після чого продовжує своє виконання.
__next__()– починає або продовжує виконання функції-генератора.send(value)– продовжує виконання та надсилає значення у функцію-генератор.throw(type[, value[, traceback]])– кидає виключення типу type в місці, де був призупинений генератор.close()– кидає виключенняGeneratorExitв місці, де був призупинений генератор.
Ні, буде помилка. Генератор не підтримує метод __getitem__.
Ключ. Порядок ключів у Python 3.7+ гарантується (порядок вставки). Для ітерації по парах ключ-значення використовується метод .items().
Метод словника .items() повертає генератор кортежів (key, value).
Сопрограма (англ. coroutine) — компонент програми, що узагальнює поняття підпрограми, який додатково підтримує безліч точок входу та зупинку і продовження виконання зі збереженням певного положення. Python 3.5 включає підтримку сопрограм на рівні мови. Для цього використовуються ключові слова async та await.
Функція dir повертає список рядків – полів об'єкта. Поле __dict__ містить словник виду {поле -> значення}.
Магічними методами називають методи, імена яких починаються та закінчуються подвійним підкресленням. Магічні вони тому, що майже ніколи не викликаються явно. Їх викликають вбудовані функції або синтаксичні конструкції. Наприклад, функція len() викликає метод __len__() переданого об'єкта.
Деякі магічні методи:
__init__: ініціалізатор класу__add__: додавання з іншим об'єктом__eq__: перевірка на рівність з іншим об'єктом__iter__: повертає ітератор
Функція super приймає клас та екземпляр:
class NextClass(FirstClass):
def __init__(self, x):
super(NextClass, self).__init__()
self.x = xТак, можна вказати більше одного батька в класі-нащадку.
MRO – method resolution order, порядок розв'язання методів. Алгоритм, за яким слід шукати метод у випадку, якщо у класу два і більше батьків.
При наслідуванні класів нового стилю застосовується правило MRO (порядок розв'язання методів), тобто лінеаризований обхід дерева класів. Такий алгоритм називається C3-лінеаризація.
При ромбовидному наслідуванні визначити метод якого класу має бути викликаний.
Міксин (mix-in), патерн проектування в ООП, коли в ланцюжок наслідування додається невеликий клас-помічник. Наприклад:
class NowMixin(object):
def now():
return datetime.datetime.utcnow()Тоді будь-який клас, успадкований з цим міксином, матиме метод now().
У Python є оператор with. Розміщений всередині код виконується з особливістю: до і після гарантовано спрацьовують події входу до блоку with та виходу з нього.
Події входу і виходу з блоку визначені методами __enter__ та __exit__. Перший спрацьовує в той момент, коли хід виконання програми переходить всередину with. __exit__ спрацьовує в момент виходу з блоку, в т.ч. і через виключення.
with open('file.txt') as f:
data = f.read()
process_data(data)Приклад реалізації власного контекстного менеджера на основі класу:
class Printable:
def __enter__(self):
print('enter')
def __exit__(self, type, value, traceback):
print('exit')Приклад реалізації з використанням бібліотеки contextlib:
from contextlib import contextmanager
@contextmanager
def printable():
print('enter')
try:
yield
finally:
print('exit')object() == object()
Завжди хибно, оскільки за замовчуванням об'єкти порівнюються за полем id (адреса в пам'яті), якщо тільки не перевизначений метод __eq__.
Класи зберігають поля та їх значення у секретному словнику __dict__. Параметр __slots__ у класі жорстко фіксує набір полів класу. Слоти використовуються коли у класу може бути дуже багато полів, або коли критична продуктивність, або коли в процесі виконання програми створюються мільйони екземплярів класу.
Недолік: не можна присвоїти класу поле, якого немає в слотах. Не працюють методи __getattr__ та __setattr__.
Поле класу з одним лідируючим підкресленням говорить про те, що параметр використовується тільки всередині класу. При цьому він доступний для звернення ззовні. Це обмеження доступу тільки на рівні угоди.
Поля з подвійним підкресленням доступні всередині класу, але недоступні ззовні. Це досягається хитрим прийомом: інтерпретатор призначає таким полям імена вигляду _<ClassName>__<fieldName>. Описаний механізм називається name mangling.
class Foo(object):
def __init__(self):
self.__bar = 42
Foo().__bar
>>> AttributeError: 'Foo' object has no attribute '__bar'
Foo()._Foo__bar
>>> 42Основна різниця між цими двома методами полягає в тому, що __new__ обробляє створення об'єкта, а __init__ обробляє його ініціалізацію.
__new__ викликається автоматично при виклику імені класу (при створенні екземпляра), тоді як __init__ викликається кожного разу, коли екземпляр класу повертається __new__.
Спочатку викликається __new__, а потім __init__.
Класи нового стилю (3.х доступні тільки вони, у 2.х при наслідуванні від object) відрізняються від класичних (за замовчуванням у 2.х) такими особливостями:
- Ромбовидні шаблони множинного наслідування мають дещо інший порядок пошуку (MRO, C3-лінеаризація).
- Класи тепер позначають типи, а типи є класами.
- Всі класи наслідують від вбудованого класу
object.
Неявна типізація, латентна типізація або качина типізація (англ. Duck typing) – вид динамічної типізації, коли межі використання об'єкта визначаються його поточним набором методів та властивостей, на противагу наслідуванню від певного класу. Тобто вважається, що об'єкт реалізує інтерфейс, якщо він містить всі методи цього інтерфейсу, незалежно від зв'язків в ієрархії наслідування.
Модуль – функціонально закінчений фрагмент програми, оформлений у вигляді окремого файлу з вихідним кодом. Файл, який містить вихідний код на мові Python, є модулем.
Назва модуля доступна в його глобальній змінній __name__. Якщо модуль не імпортований, а запущений як скрипт, то __name__ встановлюється у значення "__main__".
При імпортуванні модулів інтерпретатор Python шукає їх у директоріях та архівах, список яких доступний у змінній sys.path. За замовчуванням sys.path складається з директорії з запускуваним скриптом, вмісту змінної оточення PYTHONPATH та стандартного розташування модулів.
Модулі можуть об'єднуватися в пакети. Пакети служать як простори імен для модулів та спосіб їх структурування.
Для того, щоб директорія була пакетом, у ній має знаходитися файл __init__.py.
Обробка виняткових ситуацій або обробка виключень (англ. exception handling) — механізм мов програмування, призначений для опису реакції програми на помилки часу виконання та інші можливі проблеми (виключення), які можуть виникнути при виконанні програми.
Код на Python може згенерувати виключення за допомогою ключового слова raise.
try:
# деякий код
finally:
# деякий кодЯкщо в блоці try відбудеться помилка, блок finally все одно буде виконаний і всередині нього можна буде зробити "cleanup", наприклад.
Блоки except обробляються зверху вниз і управління передається не більш ніж одному обробнику. Тому при необхідності по-різному обробляти виключення, що знаходяться в ієрархії наслідування, спочатку потрібно вказувати обробники менш загальних виключень, а потім – більш загальних.
Якщо жоден з заданих блоків except не перехоплює виникле виключення, то воно буде перехоплено найближчим зовнішнім блоком try/except, у якому є відповідний обробник. Якщо ж програма не перехоплює виключення взагалі, то інтерпретатор завершує виконання програми та виводить інформацію про виключення в стандартний потік помилок sys.stderr.
Для того, щоб в обробнику виключення виконати певні дії, а потім передати виключення далі, використовується ключове слово raise без параметрів.
try:
1 / 0
except ZeroDivisionError:
# деяка логіка
raiseУ Python 3 при збудженні виключення в блоці except, старе виключення зберігається в атрибуті __context__. Також можна пов'язувати виключення в один ланцюжок: raise нове_виключення from старе_виключення або raise нове_виключення from None.
Блок else виконується, якщо в процесі виконання блоку try не виникло виключень. Він призначений для того, щоб відокремити код, який може викликати виключення, від коду, який не повинен потрапляти під обробку в даному блоці.
- Базові:
BaseException– базовий клас для всіх виключень.Exception– базовий клас для всіх стандартних виключень, що не вказують на обов'язкове завершення програми.ArithmeticError– базовий клас для всіх виключень, пов'язаних з арифметичними операціями.LookupError– базовий клас для виключень, пов'язаних з невірним ключем або індексом колекції.
- Деякі з конкретних стандартних виключень:
AssertionError,AttributeError,ImportError,IndexError,KeyboardInterrupt,MemoryError,NameError,NotImplementedError,OSError,OverflowError,RuntimeError,SyntaxError,SystemError,SystemExit,TypeError,ValueError,ZeroDivisionErrorта інші.
Можна. Вони мають бути нащадками класу Exception. Прийнято називати виключення так, щоб ім'я їхнього класу закінчувалося словом Error.
Декоратор у широкому сенсі – патерн проектування, коли один об'єкт змінює поведінку іншого. У Python декоратор, як правило, це функція A, яка приймає функцію B та повертає функцію C. При цьому функція C задіює в собі функцію B.
Задекорувати функцію означає замінити її на результат роботи декоратора.
Декоратором може бути будь-який викликуваний об'єкт: функція, лямбда, клас, екземпляр класу. У останньому випадку визначте метод __call__.
Застосовувати декоратор можна до будь-якого об'єкта. Найчастіше до функцій, методів та класів.
def auth_only(view):
...
@auth_only
def dashboard(request):
...Якщо в тілі функції немає оператора return, виклик поверне None. У нашому випадку декоратор поверне None і функція, яку ми декоруємо, теж стане None. При спробі викликати її після декорування отримаємо помилку "NoneType is not callable".
Перше – звичайне декорування функцією foobar.
Другий випадок – декорування функцією, яку поверне виклик foobar. Інакше це називається параметричний декоратор або фабрика декораторів.
Це функція, яка повертає декоратор. Наприклад:
from functools import wraps
def has_perm(perm):
def decorator(view):
@wraps(view)
def wrapper(request):
if perm in request.user.permissions:
return view(request)
else:
return HTTPRedirect('/login')
return wrapper
return decorator
@has_perm('view_user')
def users(request):
...wraps – декоратор зі стандартної поставки Python, модуль functools. Він призначає функції-враперу ті самі поля __name__, __module__, __doc__, що і у вихідної функції, яку ви декоруєте.
Метаклас – це «штука», яка створює класи.
Ми створюємо клас для того, щоб створювати об'єкти, так? А класи є об'єктами. Метаклас – це те, що створює ці самі об'єкти.
type – це метаклас, який Python внутрішньо використовує для створення всіх класів.
Коли ви пишете:
class Foo(Bar):
passPython робить наступне:
- Чи є у класу Foo атрибут
__metaclass__? - Якщо так, створює в пам'яті об'єкт-клас з іменем Foo, використовуючи те, що вказано в
__metaclass__. - Якщо Python не знаходить
__metaclass__, він шукає__metaclass__у батьківському класі Bar. - Якщо ж
__metaclass__не знаходиться ніде, Python використовуєtypeдля створення об'єкта-класу.
Основне застосування метакласів – це створення API. Типовий приклад — Django ORM.
Файловий об'єкт – об'єкт, що надає файл-орієнтований API (методи read(), write() тощо) для доступу до ресурсу. Файлові об'єкти також називають потоками. Файлові об'єкти є контекстними менеджерами.
У Python 3 існує три види файлових об'єктів: текстові файли (text files), «звичайні» (небуферизовані) бінарні файли та буферизовані бінарні файли.
Текстові файли записують та зчитують дані типу str та автоматично виконують перетворення кодувань та кінців рядків. Бінарні файли записують та зчитують дані типів bytes та bytearray та не виконують жодних маніпуляцій з даними.
Основні параметри:
file– ім'я файлу або файловий дескриптор;mode– режим відкриття файлу;encoding– кодування файлу;buffering– чи використовувати буферизацію.
mode може починатися з символів «r» (читання), «w» (запис), «x» (ексклюзивне створення), «a» (додавання). Також параметр mode може мати другу букву для визначення типу файлу: «t» для текстового (за замовчуванням) та «b» для бінарного.
Після закінчення роботи з файлом слід обов'язково його закрити за допомогою методу close(), особливо якщо він був відкритий для запису. При використанні буферизованого виводу дані не потрапляють у файл одразу, а записуються в буфер.
Серіалізація – це процес збереження об'єктів у двійковому або рядковому вигляді для зберігання, передачі та відновлення. Зворотній процес називається десеріалізацією.
Функція dumps модуля json зберігає JSON-представлення об'єкта в рядок. Функція dump – у текстовий файл.
Функція loads модуля json завантажує об'єкт із рядка. Функція load – з текстового файлу.
Mock на англійській означає «імітація», «підробка». Якщо потрібно тестувати функцію, то всe, що не відноситься до неї самої (наприклад, читання з диска або з мережі), можна замінити макетами-пустишками.
Модульні тести перевіряють, чи правильно працює кожен окремий модуль (юніт) вашого коду. Модульні тести не повинні перевіряти зовнішні залежності або взаємодії.
Інтеграційні тести перевіряють взаємодію між двома (або більше) окремими юнітами вашого коду. Також перевіряють інтеграцію вашого коду із зовнішніми залежностями.
Функціональне тестування може бути визначене як тестування окремих функцій модулів. Воно значно відрізняється від модульного або інтеграційного тестування; цей тип тестування проводиться більше з точки зору користувача.
Автоматизовані тести, що перевіряють роботу всієї інтегрованої системи.
Це може бути будь-який вид тесту, який пишеться після того, як була виявлена проблема. Тест має емулювати точно кроки для відтворення проблеми.
Інтеграційне тестування проводиться після модульного, а функціональне – метод тестування чорного ящика. Функціональне тестування також згадується як тестування E2E для тестування браузера.
Функціональне програмування – розділ дискретної математики та парадигма програмування, в якій процес обчислення трактується як обчислення значень функцій у математичному розумінні.
Об'єктами першого класу (англ. first-class object) у контексті конкретної мови програмування називаються сутності, які можуть бути передані як параметр, повернуті з функції, присвоєні змінній. У Python функції є об'єктами першого класу.
Функція вищого порядку – функція, що приймає як аргументи інші функції або повертає іншу функцію як результат.
Каррінг — це перетворення функції від багатьох аргументів у набір функцій, кожна з яких є функцією від одного аргументу.
def greet_curried(greeting):
def greet(name):
print(greeting + ', ' + name)
return greet
greet_hello = greet_curried('Hello')
greet_hello('German')
greet_hello('Ivan')Функція map застосовує функцію до кожного елемента послідовності. У Python 3 – повертає об'єкт-ітератор.
Функція filter залишає лише ті елементи послідовності, для яких задана функція істинна. У Python 3 – повертає об'єкт-ітератор.
Функція reduce (у Python 3 знаходиться в модулі functools) приймає функцію від двох аргументів, послідовність та опціональне початкове значення і обчислює згортку послідовності.
lru_cache– декоратор, який кешує значення функцій;partial– часткове застосування функції.
product– декартовий добуток ітераторів;permutations– генерація перестановок;combinations– генерація комбінацій;chain– з'єднання кількох ітераторів в один;takewhile– отримання значень послідовності, поки значення функції-предикату для її елементів істинне;dropwhile– отримання значень послідовності починаючи з елемента, для якого значення функції-предикату перестане бути істинним.
У будь-який момент може виконуватися тільки один потік Python. Глобальне блокування інтерпретатора — GIL — ретельно контролює виконання тредів. GIL гарантує кожному потоку ексклюзивний доступ до змінних інтерпретатора.
Проблема в тому, що через GIL далеко не всі завдання можуть бути вирішені в тредах. Навпаки, їх використання найчастіше знижує швидкодію програми (при CPU-bound задачах).
asyncio використовує сопрограми – вони виконують тільки корисну роботу, а перемикання між ними відбувається тільки в той момент, коли сопрограма очікує завершення якоїсь зовнішньої операції.
Потоки будуть простіше, якщо у вас типовий веб-додаток, який не залежить від зовнішніх сервісів. AsyncIO підійде, якщо додаток більшу частину часу витрачає на читання/запис даних.
Ключове слово async йде до def, щоб показати, що метод є асинхронним. Ключове слово await показує, що ви очікуєте завершення сопрограми.
import asyncio
import aiohttp
async def call_url(url):
async with aiohttp.ClientSession() as session:
print('Starting {}'.format(url))
async with session.get(url) as response:
data = await response.text()
return dataБагатопотоковість досягається модулем Threading. Це нативні Posix-треди, які виконуються на рівні операційної системи.
Головна відмінність у розподілі пам'яті. Процеси незалежні один від одного, мають роздільні адресні простори. Треди виконуються у спільному адресному просторі, мають спільний доступ до пам'яті, змінних, завантажених модулів.
Greenlet == Зелені треди == легковагові треди всередині віртуальної машини. Операційна система їх не бачить. Такими тредами управляє сама віртуальна машина. Приклади: модуль greenlet для Python, бібліотека gevent.
Декоратор:
def singleton(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
@singleton
class MyClass(BaseClass):
passМетакласи:
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class MyClass(BaseClass, metaclass=Singleton):
passМодуль: найпростіший варіант. Переваги: простота. Недоліки: не ініціалізується ліниво.
- Pycodestyle – перевіряє код на відповідність PEP8.
- Pylint – поєднав пошук логічних та стилістичних помилок.
- Flake8 – обгортка до pyflakes, pycodestyle, mccabe.
- autopep8 – автоматично виправляє код відповідно до PEP8.
- black – бескомпромісний форматувальник коду.
Вираз, укладений у квадратні/фігурні дужки, в якому використовуються ключові слова for та in для побудови списку/словника шляхом обробки та фільтрації елементів з одного або кількох ітерованих об'єктів. Спискове включення працює енергійно.
Є 5 кейсів використання підкреслення в Python:
- Для зберігання значення останнього виразу в REPL
- Ігнорування значення
- Для визначення спеціального значення функції або змінної
- Для використання в якості функції локалізації
- Для розділення символів числа (
1_00 == 100)
Глибока копія deepcopy() створює нову та окрему копію всього об'єкта або списку з власною унікальною адресою пам'яті. Будь-які зміни, внесені до нової копії, не будуть відображатися в оригіналі.
Поверхневе копіювання copy() також створює окремий новий об'єкт або список, але замість копіювання дочірніх елементів у новий об'єкт воно просто копіює посилання на їх адреси пам'яті.
GC (generational garbage collector) – це збирач сміття, який створювався насамперед для виявлення та видалення циклічних посилань.
CPython використовує одразу два алгоритми збирання сміття: підрахунок посилань та generational garbage collector (модуль gc).
Алгоритм підрахунку посилань є фундаментальним і не може бути вимкнений. Циклічний GC є опціональним і може бути вимкнений.
Циклічний збирач сміття розділяє всі об'єкти на 3 покоління (генерації). Нові об'єкти потрапляють у перше покоління. Якщо новий об'єкт виживає після процесу збирання сміття, він переміщається до наступного покоління. Чим вище покоління, тим рідше воно сканується на сміття.
Інтроспекція — це здатність програми досліджувати тип або властивості об'єкта під час роботи програми.
class foo(object):
def __init__(self, val):
self.x = val
def bar(self):
return self.x
dir(foo(5))
=> ['__class__', '__delattr__', ... 'bar', 'x']Рефлексія — це здатність комп'ютерної програми досліджувати та модифікувати свою структуру та поведінку (значення, мета-дані, властивості та функції) під час виконання.
# Без рефлексії
Foo().hello()
# З рефлексією
getattr(globals()['Foo'](), 'hello')()Middleware – особливий об'єкт, який зазвичай змінює вхідний запит або вихідну відповідь. Наприклад, додає заголовки, робить попередні перевірки. Middleware потрібен, коли потрібно обробити всі запити додатку.
На рівні мови це об'єкт з методами process_request та process_response. Методи мають повернути прийнятий об'єкт (запит або відповідь) для подальшої обробки або кинути виключення, якщо щось не так.
Щоб увімкнути Middleware, достатньо додати шлях до нього в список MIDDLEWARE.
SessionMiddleware– підтримка сесій. Додає до запиту об'єктsessionCsrfViewMiddleware– перевіряє, що POST-запити надіслані з поточного доменуAuthenticationMiddleware– авторизує користувача. Додає до запиту полеuserMessageMiddleware– передає користувачу короткі повідомлення
На кожен запит система генерує унікальний токен та виставляє його в куках. У кожній формі розміщується приховане поле csrf-token з цим же токеном. При надсиланні форми методом POST Django перевіряє, що поле форми та значення в куках збігаються. Якщо ні, це означає, що запит підроблений або надісланий з іншого домену.
Щоб звільнити якусь в'юху від перевірки (якщо це API, наприклад), достатньо обгорнути її декоратором csrf_exempt.
Якщо є моделі A та B зі зв'язком багато до багатьох, то створюється таблиця-міст з іменем a_to_b, яка зберігає ключ на A, ключ на B та додаткові відомості. Ця таблиця з'єднується з A та B оператором JOIN.
Форму бажано передавати методом POST з таких причин:
- GET-запити можуть бути закешовані, особливо в браузерах сімейства IE
- GET-запити осідають у логах провайдера, сервера, історії браузера. Пароль та логін у такому випадку може засвітитися в багатьох місцях
- Деякі віруси відстежують вміст адресного рядка та пересилають третім особам.
Serializer перетворює інформацію, що зберігається в базі даних та визначену за допомогою моделей Django, у формат, який легко та ефективно передається через API.
Коли користувач передає інформацію через API, серіалізатор бере дані, перевіряє їх та перетворює на щось, що Django може зберегти в екземпляр моделі. Аналогічно, коли користувач звертається до інформації через API, відповідні екземпляри передаються в серіалізатор, який перетворює їх у формат JSON.
class ThingSerializer(serializers.ModelSerializer):
class Meta:
model = Thing
fields = ('name', )Django значною мірою працює через метакласи. Коли Django конструює ваш клас, вона шукає клас з назвою Meta, щоб знати якісь параметри вашого класу (наприклад модель або поля).
У класі Meta серіалізатора можна задати модель, по якій буде створений серіалізатор, поля, які будуть включені (або exclude для виключення), list_serializer_class тощо.
Django постачається з системою аутентифікації користувачів. Вона забезпечує облікові записи користувачів, групи, права та сесії на основі кук.
Система аутентифікації складається з:
- Користувачів
- Прав: Бінарні (так/ні) прапорці, що визначають наявність у користувача права виконувати певні дії.
- Груп: Загальний спосіб призначення міток та прав на безліч користувачів.
- Налаштованої системи хешування паролів
- Інструментів для форм та представлень для аутентифікації користувачів
Common Gateway Interface. Угода про те, як веб-сервер взаємодіє з програмою, написаною на якійсь мові. Веб-сервер запускає програму як виконуваний файл. Параметри запиту передаються через змінні оточення.
Плюси: протокол не накладає умов на мову; протокол надзвичайно простий; програма не зберігає стан.
Мінуси: запуск процесу ОС на кожен запит дуже повільно; передача даних через stdout повільніша за юнікс-сокети.
- Виставляти кукам прапор
httponly. Браузер не дасть прочитати та змінити такі куки на клієнті JavaScript'ом. - Використовувати прапор
secure. Куки будуть передані тільки по захищеному з'єднанню. - Встановлювати короткий термін дії куки.
- Підписувати куки секретним ключем.
Ідентифікація: присвоєння суб'єктам та об'єктам ідентифікатора.
Аутентифікація: перевірка відповідності суб'єкта та того, за кого він намагається себе видати, за допомогою деякої унікальної інформації.
Авторизація: перевірка та визначення повноважень на виконання певних дій відповідно до раніше виконаної аутентифікації.
Всі три процедури взаємопов'язані:
- Спочатку визначають ім'я (логін або номер) – ідентифікація
- Потім перевіряють пароль (ключ або відбиток пальця) – аутентифікація
- І в кінці надають доступ – авторизація
XSS – міжсайтові запити. Сторінка, схильна до вразливості, змушує користувача виконати запит до іншої сторінки або запустити небажаний JS-код.
Вразливість усувається екрануванням небезпечних символів, чищенням (санацією) HTML-тегів.
REST (Representational state transfer «передача стану представлення») – угода про те, як вибудовувати сервіси. REST визначає 6 архітектурних обмежень:
- Єдиноманітність інтерфейсу
- Клієнт-сервер
- Відсутність стану
- Кешування
- Шари
- Код за вимогою (необов'язкове обмеження)
SOAP – протокол обміну структурованими повідомленнями в розподіленому обчислювальному середовищі. Використовує XML. Може використовуватися з будь-яким протоколом прикладного рівня: SMTP, FTP, HTTP тощо.
- REST підтримує різні формати: text, JSON, XML; SOAP – тільки XML
- REST працює тільки по HTTP(S), а SOAP може працювати з різними протоколами
- SOAP на основі читання не може бути поміщений у кеш, а REST може
- SOAP підтримує SSL та WS-security; REST підтримує тільки SSL
HTTP – текстовий протокол, що працює поверх TCP/IP. HTTP складається із запиту та відповіді.
Стартовий рядок запиту: GET /index.html HTTP/1.1
Стартовий рядок відповіді: HTTP/1.1 200 OK
Заголовки – набір пар ключ-значення. Тіло відповіді може бути порожнім або передавати пари змінних, файли, бінарні дані. Тіло відокремлюється від заголовків порожнім рядком.
Перевірити статус відповіді:
1xx: використовується вкрай рідко2xx: запит пройшов успішно3xx: перенаправлення на інший ресурс4xx: помилка з вини користувача5xx: помилка з вини сервера
Мінімальна відповідь має мати статус 301 або 302. Заголовок Location вказує адресу ресурсу, на який слід перейти.
- Заголовки
CacheтаCache-Controlрегулюють одразу кілька критеріїв кешу. - Заголовки
Last-ModifiedтаIf-Modified-Sinceзадають кешування залежно від дати оновлення документа. - Заголовок
Etagкешує документ за його унікальним хешем.
Інкапсуляція – механізм мови, що дозволяє об'єднати дані та методи, що працюють з цими даними, в єдиний об'єкт та приховати деталі реалізації від користувача.
Наслідування – механізм мови, який дозволяє описувати новий клас на основі існуючого. Наслідуванню краще надавати перевагу композиції.
Поліморфізм має кілька форм:
- Спеціальний (Ad-Hoc)
- Параметричний (в деяких мовах представлений дженериками)
- Поліморфізм підтипів (досягається за допомогою механізмів наслідування та апкасту)
Абстракція говорить, що ми повинні виділяти важливі характеристики об'єкта. Думка в тому, щоб ми могли визначити мінімально необхідний набір цих характеристик для того, щоб можна було вирішити поставлене завдання.
Принцип Keep It Stupid Simple («Дотримуйся простоти») велить стежити за тим, щоб код залишався якомога простішим.
Принцип Don't Repeat Yourself («Не повторюйся») нагадує, що кожну поведінку, що повторюється, в коді слід відокремлювати для можливості багаторазового використання.
Принцип You Aren't Gonna Need It («Тобі це не знадобиться») говорить, що небажано залишати в продакшені «точки розширення», призначені тільки для того, щоб дозволити в майбутньому легко додати новий функціонал.
Принцип Single Level of Abstraction Principle («Принцип єдиного рівня абстракцій») означає, що функції повинні мати єдиний рівень абстракції.
- S: Single Responsibility Principle – кожен клас повинен вирішувати лише одне завдання.
- O: Open-Closed Principle – програмні сутності повинні бути відкриті для розширення, але не для модифікації.
- L: Liskov Substitution Principle – підкласи можуть служити заміною для своїх суперкласів.
- I: Interface Segregation Principle – створюйте вузькоспеціалізовані інтерфейси для конкретного клієнта.
- D: Dependency Inversion Principle – об'єктом залежності має бути абстракція, а не щось конкретне.
Зв'язаність модулів (coupling) характеризує ступінь незалежності модулів. При проектуванні систем необхідно прагнути, щоб модулі мали мінімальну залежність один від одного.
Зв'язність (cohesion) характеризує цілісність, «щільність» модуля, тобто наскільки модуль є простим з точки зору його використання. В ідеалі модуль повинен виконувати одну єдину функцію.
LRU (least recently used) — це алгоритм, при якому витісняються значення, які найдовше не запитувалися. Необхідно зберігати час останнього запиту до значення. Як тільки число закешованих значень перевищує N, необхідно витіснити з кешу значення, яке найдовше не запитувалося.
Черги повідомлень є зв'язуючою ланкою між різними процесами у ваших додатках та забезпечують надійний та масштабований інтерфейс взаємодії.
Десять причин, чому черги повідомлень є важливим компонентом:
- Слабке зв'язування
- Надмірність
- Масштабованість
- Еластичність та можливість витримувати пікові навантаження
- Відмовостійкість
- Гарантована доставка
- Гарантований порядок доставки
- Буферизація
- Розуміння потоків даних
- Асинхронний зв'язок
- ActiveMQ
- RabbitMQ
- RedisQueue
- Kafka
- rocketMQ
- zeroMQ
Видалений виклик процедур (Remote Procedure Call, RPC) — клас технологій, що дозволяють комп'ютерним програмам викликати функції або процедури в іншому адресному просторі.
gRPC — це високопродуктивний фреймворк від Google для виклику віддалених процедур, що працює поверх HTTP/2. Використовує Protocol Buffers для серіалізації. Підтримує 9 мов: C, C++, Java, Go, Node.js, Python, Ruby, Objective-C, PHP, C#.
Рекурсія – коли функція викликає саму себе. Логіка рекурсивної функції, як правило, складається з двох гілок: довга гілка викликає цю ж функцію з іншими параметрами, коротка гілка визначає критерій виходу.
Рекурсія, в деяких випадках, спрощує код та робить його декларативним. Неоптимізована рекурсія призводить до накладних витрат ресурсів.
Це особливий вид рекурсії, коли функція закінчується викликом самої себе без додаткових операторів. Коли ця умова виконується, компілятор розгортає рекурсію в цикл з одним стек-фреймом.
def fact(N, acc=1):
if N == 1:
return acc
else:
return fact(N - 1, acc * N)О-велике описує швидкість роботи алгоритму (не час).
O(n).
O(log n): працює тільки з відсортованим масивом. Беремо середній елемент та перевіряємо чи він той, що ми шукаємо; якщо ні – відкидаємо половину та повторюємо.
def binary_search(list, item):
low = 0
high = len(list) - 1
while low <= high:
mid = (low + high) // 2
guess = list[mid]
if guess == item:
return mid
if guess > item:
high = mid - 1
else:
low = mid + 1
return NoneO(n * log n) (середній та найкращий випадок), O(n^2) у найгіршому.
def quicksort(array):
if len(array) < 2:
return array
else:
pivot = array[0]
less = [i for i in array[1:] if i <= pivot]
greater = [i for i in array[1:] if i > pivot]
return quicksort(less) + [pivot] + quicksort(greater)Моделює набір зв'язків. Складається з вузлів та ребер. Бувають направлені та ненаправлені, зважені та незважені.
O(V + E), де V – кількість вершин, E – кількість ребер. Допомагає відповісти на питання: чи існує шлях від вузла A до вузла B? Як виглядає найкоротший шлях від A до B?
Використовується для знаходження шляху з найменшою вагою у зваженому графі. Працює тільки в направлених ациклічних графах (DAG). Не працює з від'ємними вагами.
Використовуються коли обчислення точного рішення займає надто багато часу або коли висока точність не потрібна. Жадібні алгоритми прагнуть до локальної оптимізації з розрахунком на те, що в результаті буде досягнутий глобальний оптимум.
Застосовується для оптимізації деякої характеристики. Працює тільки в ситуаціях, коли задача може бути розбита на автономні підзадачі. У кожному рішенні з області динамічного програмування будується таблиця.
Застосовується для класифікації та регресії. У ньому використовується перевірка k найближчих сусідів.
Для обчислення відстані до сусіда використовується формула Піфагора або метрика близькості косинусів.
Куки є інформацією, що зберігається на комп'ютері веб-сайтом. Куки часто зберігають налаштування для веб-сайту. Куки зберігаються в браузері.
З ними можна працювати як з Django (request.COOKIES, response.set_cookie), так і з JavaScript (document.cookie) (якщо не встановлений прапор HTTPONLY).
Так. Значення куки може бути змінене сервером шляхом відправлення нових рядків Set-Cookie: name=newvalue.
Веб-токен JSON, або JWT – стандартизований, у деяких випадках підписаний та/або зашифрований формат упаковки даних, який використовується для безпечної передачі інформації між двома сторонами.
JWT визначає особливу структуру інформації, яка надсилається по мережі. Вона представлена в двох формах – серіалізованій та десеріалізованій.
Continuous integration (неперервна інтеграція): всі зміни, що вносяться в код, об'єднуються в центральному репозиторії кілька разів на день, і після кожного злиття спрацьовує автоматична збірка та тестування.
Continuous delivery (неперервна доставка): CI + автоматично готується та ведеться реліз до продакшену. Розгортання в продакшен виконується вручну.
Continuous deployment (неперервне розгортання): CI + CD + повністю автоматизоване розгортання в продакшен.
Основна різниця між Scrum та Kanban — у довжині ітерацій. У Scrum ітерації — 2 тижні, у Kanban задачі програмісту можна «підсовувати» хоч кожен день.
У Scrum задачі прийнято оцінювати в Story points або в годинах. У Kanban оцінка не є обов'язковою. Вважається тільки середній час на задачу (Cycle Time).
У Scrum наша ціль — закінчити спринт, у Kanban — задачу.
Технічний борг – це усвідомлене компромісне рішення, коли замовник та ключові розробники чітко розуміють всі переваги від швидкого, хоча й не ідеального технічного рішення, за яке доведеться розплатитися пізніше.
Компанії справляються з техборгом трьома способами:
- Переписують все з нуля.
- Роблять поступовий рефакторинг.
- Миряться з техборгом.
Historical Branches: замість однієї гілки master використовуються дві: master зберігає офіційну історію релізів, development – активна розробка.
Feature Branches: кожна нова функція розробляється окремо у своїй гілці, яка зливається в development.
Release Branches: коли development набере достатньо функцій для релізу, створюється release-гілка. Після підготовки вона зливається з master та тегується новою версією.
Hotfix Branches: використовуються для швидкого патчингу релізів. Це єдина гілка, що створюється з master.
Команда git rebase дозволяє поставити ваші локальні коміти після всіх комітів, що були внесені в гілку. Це дозволяє підтримувати лінійну, чисту історію комітів.
Для інтерактивного rebase (склеювання кількох комітів в один) використовується:
git rebase -i HEAD~<кількість_комітів>
Команда git cherry-pick використовується для перенесення окремих комітів з одного місця репозиторію в інше.
git cherry-pick <commit-hash>
Якщо ви змінили якісь старі коміти в історії git, при спробі push git видасть помилку. Щоб все ж запушити зміни:
git push --force-with-lease origin <ім'я_гілки>
Цей варіант кращий, оскільки не перезаписує чужі зміни, якщо хтось встиг запушити свої коміти.
Хук (hook) pre-commit дозволяє запускати власні сценарії перед створенням коміту. Використовується для:
- виконання перевірки коду на валідність (наприклад, відповідність PEP8);
- виконання комплексної перевірки проекту (юніт-тести тощо);
- переривання операції commit у разі виявлення помилок.
Транзакція є робочою одиницею роботи з базою даних. Основні концепції (властивості) транзакції описуються абревіатурою ACID:
Атомарність: будь-яка транзакція буде зафіксована тільки цілком. Якщо одна з операцій в послідовності не буде виконана, то вся транзакція буде скасована (rollback).
Узгодженість: будь-яка завершена транзакція фіксує тільки допустимі результати.
Ізольованість: кожна транзакція має бути ізольована від інших, тобто її результат не повинен залежати від виконання інших паралельних транзакцій.
Довговічність: якщо ми отримали підтвердження про виконання транзакції, то зміни не повинні бути скасовані через збій системи.
COMMIT: Зберігає зміниROLLBACK: Відкочує (скасовує) зміниSAVEPOINT: Створює точку, до якої група транзакцій може відкотитисяSET TRANSACTION: Розміщує ім'я транзакції
Читання зафіксованих даних (read committed):
- При читанні клієнт бачить тільки зафіксовані дані (ніяких «брудних» операцій читання).
- При записі можна перезаписувати тільки зафіксовані дані (ніяких «брудних» операцій запису).
Ізоляція знімків стану і відтворюване читання: Кожна транзакція читає дані з узгодженого знімка стану БД на момент її початку.
Серіалізованість (serializability): Найсильніший рівень ізоляції. Гарантує, що навіть при конкурентному виконанні транзакцій результат такий самий, як і у випадку їх послідовного виконання.
Вкладеними називаються транзакції, виконання яких ініціюється з тіла вже активної транзакції. Завершення транзакції верхнього рівня відкладається до завершення вкладених транзакцій.
Курсор у SQL – це область у пам'яті бази даних, яка призначена для зберігання останнього оператора SQL. Курсори використовуються для вибірки підмножини інформації з бази даних. У кожен момент часу прикладною програмою може бути перевірений один рядок курсора.
- MySQL часто ефективніший при використанні первинного ключа.
- PostgreSQL підтримує всі нові стандарти SQL, має більш розширений набір функцій (регулярні вирази, рекурсивні запити, успадкування таблиць).
- MySQL використовує різні рушії для зберігання даних (InnoDB, MyISAM тощо).
- PostgreSQL підтримує курсори з переміщенням, що дозволяє не завантажувати весь результат у пам'ять клієнта.
VACUUM вивільняє простір, що займається «мертвими» кортежами. При звичайних операціях Postgres кортежі, видалені або застарілі в результаті оновлення, фізично не видаляються з таблиці; вони зберігаються в ній, поки не буде виконана команда VACUUM.
EXPLAIN виводить інформацію про план виконання запиту без його виконання.
EXPLAIN ANALYZE виконує пояснюваний вираз та показує реальні результати виконання, навіть якщо це insert, update або delete.
INNER JOIN: для кожного рядка R1 з T1 в результуючій таблиці міститься рядок для кожного рядка в T2, що задовольняє умові з'єднання з R1.
LEFT OUTER JOIN: спочатку виконується INNER JOIN. Потім у результат додаються всі рядки з T1, яким не відповідають жодні рядки в T2, а замість значень стовпців T2 вставляються NULL.
RIGHT OUTER JOIN: спочатку виконується INNER JOIN. Потім у результат додаються всі рядки з T2, яким не відповідають жодні рядки в T1.
FULL OUTER JOIN: спочатку виконується INNER JOIN. Потім додаються рядки без відповідності як з T1, так і з T2.
Вважатимемо, що тайм-слот інтерв'ю – 40 хвилин.
- Уточнити вимоги та обмеження (4 хвилини)
- Зробити оцінки системи (пропускна здатність, скільки потрібно зберігати інформації, кількість серверів тощо) (3 хвилини)
- System interface – які сервіси надає система, які сервіси використовує система (3 хвилини)
- System high-level design – які компоненти входять в систему, як вони взаємодіють (5 хвилин)
- Component detailed design – детальний опис компонентів, визначення вузьких місць (15 хвилин)
- Масштабування – як система буде масштабуватися (5 хвилин)
- Summary – загальний огляд та презентація рішення (5 хвилин)
Збираємо відповіді на запитання «Що система робить?» та «Якою має бути система?»
Для підрахунку оцінок потрібно приблизно уявляти, скільки важить той чи інший тип інформації:
- Символ – 1 байт
- Метадані (рядок у базі, вага поста тощо) – 5-10 кілобайт
- Зображення 1080p – 2 мегабайти
- Відео 1080p (хвилина) – 30 мегабайт
Описуємо максимально просто – які методи будуть доступні, які параметри вони приймають.
Не варто називати конкретні технології, просто описати які компоненти будуть у системі та як вони взаємодіятимуть.
- Описуємо схему БД та запити до неї
- Перебираємо підходи з обробки даних (pros/cons)
- Вибираємо рішення та пояснюємо їх tradeoffs
- Перевіряємо вимоги
- Визначаємо edge cases
Орієнтовна інфраструктура залежно від кількості користувачів:
- 1 000 користувачів: 1 сервер, 1 БД
- 10 000: Read replicas, кілька серверів, Load balancer(s)
- 100 000: Message queue, Rate limits, Cache, CDN
- 1 000 000: Stateless services, можливо NoSQL, Database sharding
- 1 000 000 000: Regional DCs
- Що з відпусткою та лікарняними?
- Чи переноситься відпустка на наступний рік?
- Яке ставлення до офіційних державних вихідних?
- Чи є медстрахування?
- Які обмеження є на період випробувального терміну?
- Вільний графік? Чи потрібно знаходитися в офісі n годин?
- Як у компанії ставляться до дистанційної роботи?
- Чи є у компанії публікації на IT-теми та участь у IT-конференціях?
- Чи є мітапи всередині компанії?
- Зарплата в якій валюті та чи прив'язана до курсу?
- Як справи з тестуванням? Які тести ви пишете?
- Чи є Code Review? Як воно проходить?
- Чи є в проектах CI/CD? Чи є DevOps-інженер?
- Чи використовуєте ви git-flow?
- Чи використовуєте ви методологію розробки (scrum, kanban тощо)?
- Чи використовуються системи моніторингу в проектах (Sentry, NewRelic тощо)?
- Які БД використовуються в проекті? Чому саме такі?
- Яка версія мови Python використовується в проектах?
- Чи використовується технологія контейнеризації в проектах?
- Чи є сеньйори в команді?
- Як у команді приймаються рішення?
- Наскільки часто доводиться працювати з legacy?
- Які версії Python та фреймворків використовуються?