Basic Python

Что за язык Python?

Высокоуровневый с динамической строгой типизацией.

Python не разрешит сложить число 1 и строку '7' , потому ,что это значения разных типов. Нужно сначала либо сделать строку числом, либо число строкой.

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

Почему он медленный?

  1. Интерпретируемость
  2. Нету JIT-complier из коробки
  3. GIL

mutable / immutable типы данных

bool, int, float, tuple, str, frozenset, nonetype - immutable list, set, dict - mutable

Нужны оба типа для того, чтобы было меньше памяти использовано или же для дизайна.

Bounded and unbounded методы?

bounded — это тот метод, где нужно вызывать через объект класса и в методе есть self unbounded — это тот метод, которые не закреплены перед объектом и могут свободно вызываться без него (по факту staticmethod)

При измени значения int будет ли меняться id?

Да будет, потому что int — это неизменяемый тип данных

Что может быть в качестве ключа словаря?

Могут быть: целые и действительные числа, строки, кортежи. Не может быть set, list, но может быть frozenset.

args & kwargs

*args (неограниченное количество позиционных) - e.g. tuple, **kwargs (неограниченное количество именованных) - e.g. dict

Грубо говоря, *args складывает все аргументы (которые даже не имеют названий переменных, а только значения) в tuple, а **kwargs требует, чтобы мы передавали название переменной в которую хотим сложить словарь

**kwargs — это аргументы, передаваемые в вызов при помощи имени (идентификатора), либо словаря с его распаковкой при помощи **.

*args — это аргументы, передаваемые в вызов в определённой последовательности (на определённых позициях), без указания их имён.

Замыкание (closure)

Замыкание (closure) — функция, которая находится внутри другой функции и ссылается на переменные объявленные в теле внешней функции (свободные переменные).

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

У всех объектов будет приватный атрибут closure, которые возвращает кортеж из cell objects, если это функция замыкания.

Даже, если мы удалим внешнюю функцию, то вызывая объект функции все будет работать, несмотря на то, что конструкция стерта.

Декоратор с прокидыванием переменной

def my_decorator(input_arg):
    def the_real_decorator(function):
        def wrapper(*args, **kwargs):
            result = function(*args, **kwargs)
            return result
        return wrapper
 
    return the_real_decorator

functools.wraps() — функцию, которая помогает сделать декорируемую функцию похожей на исходную, делая такие вещи, как сохранение doctstring исходной функции.

Часто используется при создании декоратора.

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

Итератор — это объект, который возвращает свои элементы по одному за раз или же это итер. объект который содержит исчисляемое число значений, по которому можно итерироваться.

В python это реализовано через методы iter и next

  • iter возвращает ссылку на итератор
  • next возвращает следующий элемент

Генератор — это объект, который сразу при создании не вычисляет значения всех своих элементов. ДЕРЖИТ ОДИН. Или другими словами, это итератор элементы которого можно итерировать только один раз.

Преимущества yield:

• экономия памяти • не останавливается, а возобновляет работу

Как создать:

• Выражение-генератор можно вызывать через (i for …) • В функции вместо return использовать yield

Итератор не является генератором! Но любой генератор является итератором.

range не является генератором и там есть оператор in и там есть спец магические методы

Что такое coroutine?

Корутина — это генератор, к которому добавлена возможность послать данные через метод send. То есть их можно запустить, остановить и перезапустить.

Что такое future?

Что такое дескрипторы?

Дескриптор – это механизм, где __get__()__set__() и __delete__() перегружены, то есть смена поведения обновления, получения и удаления атрибута класса.

collections

Модуль, в котором содержутся: namedtuple, Counter, defaultdict, OrderedDict.

PYTHOPATH

Переменная среды PYTHONPATH используется в Python для указания списка каталогов, из которых можно импортировать модули. При запуске можно проверить переменную sys.path, чтобы узнать, по каким каталогам будет выполняться поиск при импорте чего-либо.

Чтобы задать эту переменную из командной строки, используйте: set PYTHONPATH=list;of;paths.

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

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

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

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

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

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

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

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

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

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

>>> {True: 'yes', 1: 'no', 1.0: 'maybe'}
{True: 'maybe'} 

Memory Management

Как создается переменная?

  1. Сначала создается объект со значением переменной
  2. Переменная начинает ссылаться на него

Memory consumption

|    | Type                      | Value        |   Consumption |
|---:|:--------------------------|:-------------|--------------:|
|  0 | <class 'int'>             | 1            |            28 |
|  1 | <class 'float'>           | 1.12         |            24 |
|  2 | <class 'decimal.Decimal'> | 1.1200000000 |           104 |
|  3 | <class 'str'>             |              |            49 |
|  4 | <class 'str'>             | 1            |            50 |
|  5 | <class 'dict'>            | {}           |            64 |
|  6 | <class 'set'>             | {1}          |           216 |
|  7 | <class 'dict'>            | {1: None}    |           232 |
|  8 | <class 'list'>            | []           |            56 |
|  9 | <class 'tuple'>           | ()           |            40 |
| 10 | <class 'list'>            | [1]          |            64 |
| 11 | <class 'int'>             | 1            |            28 |
| 12 | <class 'NoneType'>        |              |            16 |

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

Как только один из маленьких объект удаляется — память из под него не переходит операционной системе, Python оставляет её для новых объектов с таким же размером. Если в одном из выделенных блоков памяти не осталось объектов, то Python может высвободить его операционной системе.

Garbage collector

Стандартный интерпретатор питона (CPython) использует сразу два алгоритма:

– подсчет ссылок – generational garbage collector (далее GC)

1. Алгоритм подсчета ссылок. Объекты удаляются как только на них больше нет ссылок. Он постоянно работает в режиме реального времени. Не нужен объект – убрали.

Алгоритм очень простой и эффективный, но у него есть один большой недостаток. Он не умеет определять циклические ссылки. Именно из-за этого, в питоне существует дополнительный сборщик, именуемый GC, который следит за объектами с потенциальными циклическими ссылками.

МинусыПлюсы
циклические ссылкиобъекты удаляются сразу как только они не нужны
блокирование потоков
дополнительные накладные расходы на память и cpu

Проверить текущее кол-во ссылок можно через sys.getrefcount(foo)).

2. GC не работает в режиме реального времени и запускается периодически. Сборщик мусора разделяет все объекты на 3 поколения (генерации). Новые объекты попадают в первое поколение. Если новый объект выживает процесс сборки мусора, то он перемещается в следующее поколение. Чем выше поколение, тем реже оно сканируется на мусор.

Чтобы узнать порог отсеивания: gc.get_threshold()(700, 10, 10).

Useful libs:

Какой метод разрешения коллизии используется в Python?

В Python используется метод открытой адресации.

More about collisions → Computer Science

OOP

Property

Обеспечивает интерфейс для атрибутов экземпляра класса. Он инкапсулирует атрибуты экземпляров и позволяет устанавливать setter, getter, deleter.

Classmethod vs Staticmethod

classmethodstaticmethod
Принимает cls как первый аргумент.Не принимает дополнительных параметров.
можно обращаться только к атрибутам класса, но не объекта.Через этот метод нельзя обратиться ни к объектам, ни к методам класса.
Используем тогда, когда нужен доступ только к методам класса. Может быть использован как фабрика-метод, который будет возвращать объект класса.Используем тогда, когда ни используется объект класса, ни атрибуты класса. Utility-type методы, которые ничего не знают о классе.

Dataclass

Используем, когда хотим хранить какие-то данные внутри класса. Следующие методы будут автоматически сгенерированы и код будет короче:

  • init
  • repr
  • eq

MRO

Это набор правил, по которому формируется схема наследования.

В python 2 алгоритм поиска в глубину: от текущего класса к родительскому и выше В python 3 алгоритм поиска в ширину: текущий класс и по всем его родителям

Можно вызвать через Class.mro(), чтобы увидеть схему наследования.

Diamond problem

Коренной класс А имеет детей B & C, а D наследуется от B & C.

Untitled

Mixin

Это небольшие классы, которые используются для того, чтобы дать дополнительный функционал другому классу. Например, полезно, когда:**

  • хотим добавить множество функций для класса
  • хотим использовать конкретную функцию во множестве разных классов

При создании класса в компании дописываем Mixin.

Metaclasses

Метаклассы — шаблоны для создания классов. Объекты создаются классом, а классы создаются метаклассом. Методы: prepare (подготавливает данные), new(создает класс), init(инициализацию), call (создание класса)

Например, type — это метакласс.

Зачем нужен метод super?

Позволяет обращаться к классу-родителю. Основное ее применение и польза – получения доступа из класса наследника к методам класса-родителя в том случае, если наследник переопределил эти методы.

Например, мы хотим внести какие-то изменения в дочернем классе не вызывая материнский объект. Вместо этого мы пишем super.our_method()

Чаще всего в init используется.

Что такое магические методы? Какие магические методы есть и для чего используются?

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

Конструирование и инициализация

  1. __new__(cls, [...) — это первый метод, который будет вызван при инициализации объекта. Он принимает в качестве параметров класс и потом любые другие аргументы, которые будут переданы в __init__; переписывается для кастомизации создании класса и возвращает инстанс класса
  2. __init__(self, [...) — инициализатор класса.
  3. __del__ — это его деструктор

Магические методы сравнения

__eq__(self, other)Определяет поведение оператора равенства, ==.

Числовые магические методы

__abs__(self)Определяет поведение для встроенной функции abs(). • __round__(self, n)Определяет поведение для встроенной функции round()n это число знаков после запятой, до которого округлить.

Представление своих классов

__str__(self)Определяет поведение функции str(), вызванной для экземпляра вашего класса. • __repr__(self) делает то же самое, что и __str__(self), но главное отличие от str() в целевой аудитории. repr() больше предназначен для машинно-ориентированного вывода (более того, это часто должен быть валидный код на Питоне), а str() предназначен для чтения людьми. • __dir__(self)Определяет поведение функции dir(), вызванной на экземпляре вашего класса. Этот метод должен возвращать пользователю список атрибутов.

Зачем нужен метод super?

Позволяет обращаться к классу-родителю. Основное ее применение и польза – получения доступа из класса наследника к методам класса-родителя в том случае, если наследник переопределил эти методы.

Например, мы хотим внести какие-то изменения в дочернем классе не вызывая материнский объект. Вместо этого мы пишем super.our_method()

Чаще всего в init используется.

Модификаторы доступа в python

__private существует только внутри класса, даже с наследованием невозможно получить _protected можно пронаследовать

Через protected доступ будет в текущем классе и в дочерних классах, но снаружи вызвать его будет нельзя. Отличие в том, что через private доступа не будет и будет кидать AttributeError, а в protected доступ все еще будет.

По факту вызвать все равно можно даже private через objectclass._Class__privateatr

Concurrency

GIL – зачем нужен?

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

Thread

Пото́к — наименьшая единица обработки, исполнение которой может быть назначено ядром операционной системы. Поток выполнения находится внутри процесса.

Mutex

A mutual exclusion lock or mutex lock is a synchronization primitive intended to prevent a race condition. Mutex следит за тем, чтобы выполнялся только один тред.

threading.Lock() - mutex in Python

# create a lock
lock = Lock()
# acquire the lock
lock.acquire()
# release the lock
lock.release()

Semaphore

Инструмент, который ограничивает количество потоков, которое может быть использовано Lock() объектом. По факту это расширение мьютекса.

То есть, если в семафоре стоит “1”, то он работает также как и мьютекс.

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

  • кол-во калькуляций
  • кол-во открытых сокетов
  • кол-во операций I/O

Race condition

Два треда запускают один и тот же код и могут получить доступ, либо изменять один и тот же ресурс, оставляя ресурс в непонятном виде.

Deadlock

A deadlock is a concurrency failure mode where a thread or threads wait for a condition that never occurs.

Например:

  • поток A waits for B and B waits for A
  • поток ждет сам себя
  • неправильное расположение acquire среди потоков
  • после acquire потока-1 происходит exception и до release не доходит

Чтобы избежать блокировок:

  • Использовать контексные менеджеры
  • Использовать timeout
  • Всегда acquire locks в верном порядке

Reference:

How to Identify a Deadlock in Python


Сложность операций в коллекциях

list

OperationAverage Casehttp://en.wikipedia.org/wiki/Amortized_analysis
CopyO(n)O(n)
Append[1]O(1)O(1)
Pop lastO(1)O(1)
Pop intermediate[2]O(n)O(n)
InsertO(n)O(n)
Get ItemO(1)O(1)
Set ItemO(1)O(1)
Delete ItemO(n)O(n)
IterationO(n)O(n)
Get SliceO(k)O(k)
Del SliceO(n)O(n)
Set SliceO(k+n)O(k+n)
Extend[1]O(k)O(k)
http://svn.python.org/projects/python/trunk/Objects/listsort.txtO(n log n)O(n log n)
MultiplyO(nk)O(nk)
x in sO(n)
min(s), max(s)O(n)
Get LengthO(1)O(1)

collections.deque represented internally as a doubly linked list.

OperationAverage CaseAm. Worst Case
CopyO(n)O(n)
appendO(1)O(1)
appendleftO(1)O(1)
popO(1)O(1)
popleftO(1)O(1)
extendO(k)O(k)
extendleftO(k)O(k)
rotateO(k)O(k)
removeO(n)O(n)
Get LengthO(1)O(1)

set

OperationAverage caseWorst Case
x in sO(1)O(n)
Union sthttps://wiki.python.org/moin/TimeComplexity_%28SetCode%29
Intersection s&tO(min(len(s), len(t)))O(len(s) * len(t))
Multiple intersection s1&s2&..&sn(n-1)*O(l) where l is max(len(s1),..,len(sn))
Difference s-tO(len(s))
s.difference_update(t)O(len(t))
Symmetric Difference s^tO(len(s))O(len(s) * len(t))
s.symmetric_difference_update(t)O(len(t))O(len(t) * len(s))

dict

OperationAverage CaseAmortized Worst Case
k in dO(1)O(n)
Copy[3]O(n)O(n)
Get ItemO(1)O(n)
Set Item[1]O(1)O(n)
Delete ItemO(1)O(n)
Iteration[3]O(n)O(n)

Reference: TimeComplexity - Python Wiki