CS > Python > Flashcards
Python Flashcards
Which data structures are mutable?
list, dict, set, custom classes
Which data structures are immutable?
bool, int, float, tuple, str, frozenset
Библиотека collections:
namedtuple() - подкласс кортежей с именованными полями
deque - контейнер, похожий на список, с более быстрыми append и pop
ChainMap - словароподобный класс для одновременного просмотра нескольких mappings
Counter - словароподобный класс для хешируемых объектов
OrderedDict - словарь, который запоминает порядок добавления элементов
defaultdict - словарь, который заполняет недостающие значения
UserDict - обертка для словарей
UserList - обертка для списков
UserString - обертка для строк
Где будет быстрее поиск и почему: dict, list, set, tuple?
Средняя временная сложность поиска в множествах и словарях соответствует O(1), в случае последовательностей O(n). Кортежи – это неизменяемый тип, поэтому они могут давать выигрыш в скорости перед списками.
В чем разница между списком и массивом?
Список (list) может содержать элементы разного типа (числа, строки), массив содержит только одного типа.
Как создать список?
с помощью квадратных скобок [] или list()
Как добавить элементы в список?
list. append(x) - в конец
list. insert(index, x)
Обращение к элементам списка?
по индексу, срезом list_name[start:stop:step], итерированием (цикл for без счетчика)
Как удалить элемент из списка?
list. remove()
list. pop() - удаление и возвращение последнего
Использование списков?
- Хранить объекты данных разных типов
- Сохранять очередность элементов и порядок вставки
- Хранить повторяющиеся значения
- Изменять элементы
Как удалить повторяющиеся элементы списка, сохранив порядок следования остальных?
lst = ["a", "b", "a", "c", "c"] lst = list(dict.fromkeys(lst))
Почему при изменении списка в цикле используется конструкция вида for x in x in lst[:]?
Здесь применяется оператор среза [:], то есть создается копия. Поэтому изменения в цикле не влияют на оригинальный список.
Как получить конкатенацию кортежей, выкинув неуникальные элементы?
Через множества: tuple(set((1, 2)) ^ set((2, 3)))
Есть два списка одинаковой длины. В одном – ключи, в другом – значения. Как составить словарь?
dict(zip([‘key1’, ‘key2’], [‘value1’, ‘value2’]))
Как инвертировать словарь, то есть поменять местами пары ключ-значения?
d = {‘a’: 1, ‘b’: 2}
dict(zip(d.values(), d.keys()))
Что делает функция id()?
Возвращает идентификатор переданного объекта, уникальный на время его существования.
Как получить список всех атрибутов объекта?
С помощью функции dir()
В чем отличие (i for i in arr) от [i for i in arr]?
Слева – выражение-генератор, справа – генератор списка (list comprehension). Генератор списка целиком создает список в памяти, а выражение-генератор – по мере необходимости. Итератор является более общей концепцией, чем генератор, и представляет собой любой объект, класс которого имеет методы __next__ и __iter__. Генератор – это итератор, который обычно создается путем вызова функции, содержащей не менее одного оператора yield. Это ключевое слово действует аналогично return, но возвращает объект-генератор.
Что такое анонимные функции? Где они могут быть полезны?
В Python анонимные функции создаются при помощи лямбда-выражений. Такие выражения удобно использовать в местах, где ожидается функция с достаточно ограниченной задачей.
Что такое магические методы?
Так называют специальные методы, обрамленные двумя подчеркиваниями. Магические методы представляют простой способ заставить объекты вести себя аналогично встроенным типам. Это, в частности, позволяет стандартизировать поведение базовых операторов с экземплярами класса.
How to use f-string?
print(f’His name is {name}’)
print(f’His name is {name!r}’)
print(f’{book:{10}} {author:{8}} {pages:.>{7}}’)
How to get back to the beginning when reading a file?
file.seek(0)
How to write into a file without saving the original?
my_file = open(‘test.txt’,’w+’) ‘w’ for writing, ‘w+’ for reading and writing
How to add content into a file without deleting the original?
my_file = open(‘test.txt’,’a+’) ‘a’ for appending, ‘a+’ for reading and appending
What context manager is used for files?
with open(‘test.txt’,’r’) as txt:
Which library is used for PDF files?
PyPDF2
How to read a PDF?
import PyPDF2 f = open('file.pdf','rb') pdf_reader = PyPDF2.PdfFileReader(f) page = pdf_reader.getPage(x) page_ext = page_.extractText()
How to write into PDF?
f = open('file.pdf','rb') pdf_reader = PyPDF2.PdfFileReader(f) page = pdf_reader.getPage(x) pdf_writer = PyPDF2.PdfFileWriter() pdf_writer.addPage(page) pdf_output = open("Some_New_Doc.pdf","wb") pdf_writer.write(pdf_output) pdf_output.close() f.close()
How to separate groups of regular expressions?
phone_pattern = re.compile(r’(\d{3})-(\d{3})-(\d{4})’)
results = re.search(phone_pattern,text)
results.group() # 111-222-1234
results.group(1) # 111
How ML algorithms are exposed in scikit-learn?
via Estimator
How to import a ML model from scikit-learn?
from sklearn.family import model
How to check for missing values in data set?
import pandas as pd
df = pd.read_csv(‘../TextFiles/smsspamcollection.tsv’,sep=’\t’)
df.isnull().sum()
How to count unique values of a column in a data set?
import pandas as pd
df = pd.read_csv(‘../TextFiles/smsspamcollection.tsv’,sep=’\t’)
df[‘column_name’].value_counts()
How to split a data set into train set and test set?
from sklearn.model_selection import train_test_split # X feature data X = df[['length','punct']] # y label y = df['label'] X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.3,random_state=42)
How to fit a model using train set?
from sklearn.linear_model import LogisticRegression
lr_model = LogisticRegression(solver=’lbfgs’)
lr_model.fit(X_train, y_train)
How to evaluate a model on a test set?
from sklearn import metrics
prediction = lr_model.predict(X_test)
print(metrics.confusion_matrix(y_test, prediction))
print(metrics.classification_report(y_test, prediction))
sending requests with parameters to website
import requests
url = ‘https://wttr.in’ # не изменяйте значение URL
weather_parameters = { '0': '', 'T': '', 'M': '', 'lang': 'ru' # добавьте параметр запроса `T`, чтобы вернулся чёрно-белый текст }
response = requests.get(url, params=weather_parameters) # передайте параметры в http-запрос
print(response.text)
build a dataframe in pandas
atlas = [ ['Франция','Париж'], ['Россия','Москва'], ['Китай','Пекин'], ['Мексика','Мехико'], ['Египет','Каир'], ] geography = ['country', 'capital'] world_map = pd.DataFrame(data = atlas , columns = geography) # таблица сохраняется в переменной с произвольно выбранным именем world_map print(world_map) # вывод на экран
rename column names in dataframe (pandas)
df.set_axis(new_names,axis=’columns’,inplace=True)
filling NaN columns in dataframe (pandas)
cholera[‘imported_cases’] = cholera[‘imported_cases’].fillna(0)
Чем new отличается от init?
Метод __new__ используется, когда нужно управлять процессом создания нового экземпляра, а __init__ – когда контролируется его инициализация. Поэтому new возвращает новый экземпляр класса, а init – ничего.
Какая разница между одинарным (_) и двойным (__) подчеркиванием перед именем объекта?
Одинарным подчеркиванием задаются частные переменные, функции, методы и классы. Все эти объекты будут проигнорированы при импорте с помощью from module import * Двойное подчеркивание применяется для искажения имен атрибутов в классе (вызвать такой метод стандартным образом не получится).
Как в Python воплощены public, private, protected методы?
Все компоненты класса Python по умолчанию являются открытыми (public). Для защищенных (protected) методов по соглашению Python добавляется префикс одиночного подчеркивания, для закрытых (private) методов – префикс двойного подчеркивания.
Что такое MRO?
MRO – сокращение от method resolution order. То есть это способ разрешения проблемы множественного наследования классов. Для конструирования линеаризации класса в версиях Python с новым стилем классов используется алгоритм C3 линеаризации.
C3 linearization algorithm enforces following 2 constraints
- Children precede their parents
- If a class inherits from multiple classes, they are kept in the order specified in the tuple of the base class.
Финальный список формируется так: сначала добавляется данный класс, потом рассматривается первый класс из линеаризации первого родителя, если он не встречается в других списках, то добавляется в финальный и так далее. Если участвует, то переходим к рассмотрению следующего родителя.
Для чего в Python используются слоты?
Магический атрибут __slots__ позволяет задать ограниченный набор атрибутов, которыми будет обладать экземпляр класса. За счет такого ограничения можно повысить скорость работы при доступе к атрибутам и сэкономить место в памяти.
Что такое контекстные менеджеры? Где они применяются? Как создать свой контекстный менеджер?
Контекстные менеджеры – это конструкции, которые упрощают работу с тем или иным интерфейсом. Например, работу с файлами или базами данных. Создаются они с помощью оператора with. Для создания собственного класса контекстного менеджера используется библиотека contextlib.
Что такое декораторы с параметрами?
В качестве декоратора можно использовать выражение, значение которого – функция, принимающая и возвращающая функцию. А значит, можно создавать декораторы с параметрами (фабрики декораторов).
Чем отличаются и как используются декораторы @classmethod и @staticmethod?
Базовые декораторы classmethod, staticmethod используются для методов, определённых внутри классов. В метод класса первым аргументом передаётся класс. Аналогично метод экземпляра в первом аргументе получает сам экземпляр. Статичный метод используется в том случае, когда метод не имеет доступа к тому, что представляет собой класс или объект класса.
Что такое дескриптор?
Дескриптор – атрибут объекта, чьё поведение при доступе переопределяется методами __get__, __set__ и __delete__. Если определен хотя бы один из этих методов, объект становится дескриптором.
Что такое метаклассы в Python?
В Python классы являются объектами, поэтому они сами должны чем-то генерироваться. Эти конструкции представляют собой своеобразные «классы классов» и называются метаклассами. Примером встроенного метакласса является type. В основном метаклассы используются для создания API.
Классы – это объекты, создающие экземпляры. Классы сами являются экземплярами метаклассов..
Как тестировать код? Что такое mock?
Для модульного тестирования используется unittest. Mock – это специальный модуль (ставший недавно частью стандартной библиотеки) для тестирования без существенной адаптации кода под тесты. Unittest поддерживает автоматизацию тестов, использование общего кода для настройки и завершения тестов, объединение тестов в группы, а также позволяет отделять тесты от фреймворка для формирования отчётов. Модуль unittest представляет классы, упрощающие поддержку этих качеств для набора тестов. Ключевые элементы: испытательный стенд (test fixture); тестовый случай (test case); набор тестов (test suite); исполнитель тестов (test runner). Использование unittest: import unittest
class TestSum(unittest.TestCase):
def test_sum(self): self.assertEqual(sum([1, 2, 3]), 6, "Should be 6")
if __name__ == ‘__main__’:
unittest.main()
Nose
Совместим с любыми тестами, написанными с использованием unittest. Чтобы начать тестирование Python-кода, установите его из PyPl и выполните в командной строке. Он попытается обнаружить все скрипты с именем test*.py, наследующие от unittest.
$ pip install nose2
$ python -m nose2
Pytest
Pytest также поддерживает выполнение тестов unittest, а его преимущество заключается в написании своих тестов. Они представляют собой ряд функций в файле Python. Кроме того, он отличается:
Поддержкой встроенного утверждения assert вместо использования специальных методов self.assert*().
Возможностью повторного запуска с пропущенного теста.
Наличием системы дополнительных плагинов.
Как проводится отладка программ на Python?
В Python есть модуль pdb. Он позволяет пошагово провести отладку программы. В версии 3.7 появилась функция breakpoint(), которая также облегчает дебаггинг.
def divide(e, f): import pdb; pdb.set_trace() == breakpoint() return f / e
Какие есть программы для проверки стиля кода? Каковы их преимущества и недостатки?
Популярные: pylint, pyflakes
Весь инструментарий можно условно разделить на две группы по способу реагирования на ошибки. Первая группа сообщает о найденных ошибках, перекладывая задачу по их исправлению на программиста. Вторая — предлагает пользователю вариант исправленного кода или автоматически вносит изменения.
И первая, и вторая группы включают в себя как простые утилиты командной строки для решения узкоспециализированных задач (например, проверка docstring или сортировка импортов), так и богатые по возможностям библиотеки, объединяющие в себе более простые утилиты. Средства анализа кода из первой группы принято называть линтерами (linter). Программы второй группы называют форматировщиками (formatter). При применении линтеров программисту, во-первых, необходимо писать код с оглядкой, дабы позже не исправлять найденные ошибки. И во вторых, принимать решение по поводу обнаруженных ошибок — какие требуют исправления, а какие можно проигнорировать. Форматировщики, напротив, автоматизируют процесс исправления ошибок, оставляя программисту возможность осуществлять контроль.
Pycodestyle — простая консольная утилита для анализа кода Python, а именно для проверки кода на соответствие PEP8. Один из старейших анализаторов кода. Формат вывода прост и содержит только необходимую информацию:
: ::
Возможности программы по проверке соглашений ограничены: нет проверок на правильность именования, проверка документации сводится к проверки длины docstring. Тем не менее функционал программы нельзя назвать “спартанским”, он позволяет настроить необходимый уровень проверок и получить различную информацию о результатах анализа.
Pydocstyle проверяет наличие docstring у модулей, классов, функций и их соответствие официальному соглашению PEP257. Функционал pydocstyle практически идентичен описанному выше для pycodestyle, различия касаются лишь названий ключей.
Pyflakes не делает проверок стиля. Цель этого анализатора кода — поиск логических и синтаксических ошибок. Разработчики pyflakes сделали упор на скорость работы программы, безопасность и простоту. Несмотря на то, что данная утилита не импортирует проверяемый файл, она прекрасно справляется c поиском синтаксических ошибок и делает это быстро. С другой стороны, такой подход сильно сужает область проверок.
Функциональность pyflakes — “нулевая”, все что он умеет делать — это выводить результаты анализа в консоль
Pylint
Pylint совместил в себе проверку на наличие логических и стилистических ошибок. Этот мощный, гибко настраиваемый инструмент для анализа кода Python отличается большим количеством проверок и разнообразием отчетов. Это один из самых “придирчивых” и “многословных” анализаторов кода. Анализ нашего тестового скрипта выдает весьма обширный отчет, состоящий из списка найденных в ходе анализа недочетов, статистических отчетов, представленных в виде таблиц, и общей оценки кода
Vulture — небольшая утилита для поиска “мертвого” кода в программах Python. Она использует модуль ast стандартной библиотеки и создает абстрактные синтаксические деревья для всех файлов исходного кода в проекте. Далее осуществляется поиск всех объектов, которые были определены, но не используются. Vulture полезно применять для очистки и нахождения ошибок в больших базовых кодах.
Flake8 — комбайн нескольких утилит, pyflakes, pycodestyle, mccabe. Flake8 имеет схожий с pylint основной функционал. Однако она имеет ряд отличий и особенностей:
— Возможности статистических отчетов ограничены подсчетом количества каждой из ошибок (–statistics) и их общим количеством (–count)
Для запуска в несколько потоков (–jobs=) используется модуль multiprocessing, по этой причине многопоточность не будет работать на Windows системах.
— Отсутствует возможность генерации отчетов в формате json, при вызове с ключом –bug-report создается только заголовок для отчета с указанием платформы и версий входящих утилит.
— Комментарии в коде блокирующие вывод. Добавление в строку с ошибкой комментария # noqa, уберет ее из отчета.
— Во время редактирования для подавления отдельных ошибок “на лету” можно перечислить исключаемые ошибки в ключе –extend-ignore=
— Проверка синтаксиса в строках doctest (–doctests).
Следующие возможности Flake8 можно отнести к его достоинствам. Наличие их сделало Flake8 весьма популярным инструментом среди разработчиков:
— Наличие Version Control Hooks. Интеграция с системами контроля версий происходит буквально с помощью двух команд (поддерживаются git и mercurial). Настройка hook позволяет не допускать создания коммита при нарушении каких-либо правил оформления.
— Расширяемость. Flake8 для анализа кода Python позволяет создавать и использовать плагины. С помощью плагинов в Flake8 можно: добавить дополнительные проверки, использовать другие форматы отчетов или автоматически исправлять найденные ошибки. На PyPi можно найти большое количество open-source плагинов.
Prospector объединяет функциональность других инструментов анализа Python, таких как pylint, pep8, mccabe, Pyflakes, Dodgy, pydocstyle (экспериментально, возможны ошибки). Дополнительно можно подключить mypy, pyroma, vulture. Главной особенностью prospector является наличие предустановленных профилей, которые содержат настройки входящих в него утилит, призванных подавить наиболее придирчивые предупреждения и оставить только важные сообщения. Это позволяет начать работу с prospector без длительной настройки. Часть профилей отличаются уровнем “строгости” к требованиям. Из коробки доступно пять таких профилей: verylow, low, medium, high и veryhigh. Для указания этих профилей при запуске анализатора предназначен ключ –strictness
Pylama — инструмент аудита кода для Python и JavaScript. Служит оберткой на такими утилитами как: pydocstyle, pycodestyle, pyflakes, mccabe, pylint, radon (инструмент для сбора и вычисления различных метрик из исходного кода). Для работы с работы с JavaScript кодом используется gjslint. Из доступных настроек стоит выделить возможность запускаться в асинхронном режиме интеграцию с pytest (пакет автоматически регистрируется как плагин pytest во время установки).
autopep8 модифицирует код, не совместимый с PEP8. Проверка соответствия соглашениям осуществляется с помощью утилиты pycodestyle. В autopep8 есть поддержка многопоточности, рекурсивного обхода каталогов, возможность сохранения настроек в файле, задание диапазона строк для исправления, фильтрация ошибок и непосредственное изменение проверяемого файла. По умолчанию программа выводит исправленный код в виде текста на консоль. autopep8 отлично справляется задачей форматирования, следя за несоблюдением PEP8. Однако невозможность подключения собственных стилей ограничивает круг его применения.
Yapf - По функционалу yapf похож на autopep8, но использует другой подход, который основан на «clang-format», разработанном Дэниелом Джаспером. Отформатированный yapf код, будет не только соблюдать принятые соглашения, но и выглядеть так, словно был написан хорошим программистом. Вторым важным отличием является возможность задавать стили. Для этого воспользуйтесь ключом –style и в качестве аргумента передайте файл с настройками или одно из предопределенных значений (pep8, google, chromium, facebook).
black:
- игнорирование не модифицированных файлов, программа запоминает, какие файлы она изменяла и при следующем запуске форматирует только файлы с внесенными изменениями;
- возможность запретить изменение отдельных блоков в коде, для этого используются комментарии: # fmt: off и # fmt: on, обозначающие начало и конец блока;
- длина строки по умолчанию является 88 символов, что не соответствует официальному соглашению PEP8;
- дополнительно можно установить HTTP сервер blackd, который позволяет избежать накладных расходов на создание процесса каждый раз, когда мы хотим отформатировать файл. Исходный код передается в теле POST запроса, а флаги управления форматированием в заголовках (флаги идентичны ключам командной строки, используемых при запуске black).
В чём состоит отличие процессов от потоков?
Модули – это subprocess и threading. Использование нескольких процессов аналогично использованию нескольких независимых программ, обмен данными организован через каналы. Если приложение должно выполнять несколько задач в одно и то же время, используются потоки (threads). В этом случае для ограничения доступа потоков к памяти в Python имеется блокировщик GIL (Global Interpreter Lock)
Что такое AsyncIO? Когда его имеет смысл использовать?
В отличие от потоков, в AsyncIO переключение между сопрограммами происходит лишь тогда, когда сопрограмма ожидает завершения внешней операции. AsyncIO подойдет, если приложение большую часть времени тратит на чтение/запись данных, а не их обработку, то есть, например, для работы с веб-сокетами.
В чем заключается проблема циклических зависимостей? Как ее решить?
Циклические зависимости обычно служат признаком некачественного проектирования системы. Временное решение – перенести импорт во вложенные области видимости. В частности, определения функций.
Рассмотрите два подхода ниже для инициализации массива и массивов. В чём разница между этими подходами и почему вам следует использовать только один из них?
# Инициализация массива – метод 1
…
x = [[1,2,3,4]] * 3
x
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
# Инициализация массива -- метод 2 ... y = [[1,2,3,4] for _ in range(3)] y [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
Какой метод следует использовать и почему?
Второй метод, как и ожидалось, создаёт массив из трёх элементов, каждый из которых является независимым четырехэлементным массивом. В первом же методе все члены массива указывают на один и тот же объект.
Почему лучше использовать массивы NumPy вместо списков Python?
NumPy — пакет Python для научных вычислений, который способен работать с большими объёмами данных. Он включает в себя мощный N-мерный объект массива и набор продвинутых функций.
Также есть определённые причины, согласно которым лучше использовать массивы NumPy:
массивы NumPy более компактны, чем списки;
запись и чтение выполняются быстрее;
массивы более эффективны из-за увеличения функциональности списков.
Зачем использовать декораторы функций?
Декораторы — вызываемые объекты в Python, которые используются для изменения или расширения функции или класса. Прелесть декораторов в том, что один декоратор может быть применён к нескольким функциям (или классам). Например, во Flask используются декораторы как механизм добавления новых портов в веб-приложении. Примеры некоторых наиболее распространённых применений декораторов включают добавление синхронизации, принудительное введение типов, логирование, предусловия и постусловия для класса или функции.
- Декораторы несколько замедляют вызов функции
- Вы не можете “раздекорировать” функцию.
- Декораторы оборачивают функции, что может затруднить отладку. Последняя проблема частично решена добавлением в модуле functools функции functools.wraps, копирующей всю информацию об оборачиваемой функции (её имя, из какого она модуля, её документацию и т.п.) в функцию-обёртку.
Декораторы могут быть использованы для расширения возможностей функций из сторонних библиотек (код которых мы не можем изменять), или для упрощения отладки (мы не хотим изменять код, который ещё не устоялся). Также полезно использовать декораторы для расширения различных функций одним и тем же кодом, без повторного его переписывания каждый раз
Что такое @classmethod, @staticmethod, @property?
Декораторы @classmethod, @staticmethod и @property используются для функций, определённых внутри классов.
Методы класса всегда принимают класс как первый аргумент
Статичные методы не имеют аргументов кроме тех, которые вы им назначаете при вызове
Декораторы properties реализуются с помощью геттеров и сеттеров. Вызывать их открыто — ошибка
# Атрибуты “только для чтения” могут быть определены геттером без сеттера
Создайте логируемый декоратор
import logging
def log(func):
“””
Логируем какая функция вызывается
“””
def wrap_log(*args, **kwargs): name = func.\_\_name\_\_ logger = logging.getLogger(name) logger.setLevel(logging.INFO)
# Открываем файл логов для записи fh = logging.FileHandler("%s.log" % name) fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' formatter = logging.Formatter(fmt) fh.setFormatter(formatter) logger.addHandler(fh) logger.info("Вызов функции: %s" % name) result = func(*args, **kwargs) logger.info("Результат: %s" % result) return func return wrap_log
@log def double_function(a): """ Умножаем полученный параметр """ return a*2
Этот небольшой скрипт содержит функцию log, которая принимает функцию как единственный аргумент. Мы создаем объект логгер, а название лог-файла такое же, как и у функции. После этого функция log будет записывать, как наша функция была вызвана и что она возвращает, если возвращает.
Логирование
import logging
# add filemode="w" to overwrite logging.basicConfig(filemode='w',filename="sample.log", level=logging.INFO)
logging. debug(“This is a debug message”)
logging. info(“Informational message”)
logging. error(“An error has happened!”)
Существует пять уровней логирования (в порядке возрастания): DEBUG, INFO, WARNING, ERROR и CRITICAL.
Иерархия исключений. Какие системные исключения вам знакомы?
Иерархия исключений. Какие системные исключения вам знакомы?
Исключение, которое вы не видите при выполнении кода — BaseException — это базовое исключение, которое наследует остальные.
В иерархии исключений выделяют две основные группы:
Системные исключения и ошибки.
Пользовательские исключения.
К системным относятся:
SystemExit — исключение, порождаемое функцией sys.exit при выходе из программы;
KeyboardInterrupt — возникает при прерывании программы пользователем (обычно сочетанием клавиш Ctrl + C).
GeneratorExit — возникает при вызове метода close объекта generator.
Что такое итератор?
Итератор — интерфейс, предоставляющий доступ к элементам коллекции (массива или контейнера) и навигацию по ним. В различных системах итераторы могут иметь разные общепринятые названия. В терминах систем управления базами данных итераторы называются курсорами. В простейшем случае итератором в низкоуровневых языках является указатель.
В чём разница между итератором и генератором?
Эти термины тесно связаны (любой генератор — это итератор), их довольно часто путают, что иногда приводит к недопониманию. Итератор — более общая концепция. Это объект, у которого определены два метода: __next__ и __iter__. С другой стороны, генератор — это итератор. Но не наоборот. Генератор может получаться использованием ключевого слова yield в теле функции.
def squares(start, stop): for i in range(start, stop): yield i * i generator = squares(a, b)
GIL
Концепция GIL заключается в том, что в каждый момент времени только один поток может исполняться процессором. Это сделано для того, чтобы между потоками не было борьбы за отдельные переменные. Исполняемый поток получает доступ ко всему окружению. Такая особенность реализации потоков в Python значительно упрощает работу с потоками и дает определенную потокобезопасность (thread safety).
Для чего служит ключевое слово «self»?
Ключевое слово self — переменная, которая относится к экземпляру объекта. Когда создаётся класс, явная ссылка на объект того же типа класса отсутствует. Поэтому, чтобы ссылаться на текущий класс или объект, в Python используется ключевое слово self.
class User: def \_\_init\_\_(self): self.name = 'Ivan Ivanov' self.age = 16
user_obj = User()
user_obj.name # self.name содержит ‘Ivan Ivanov’ в качестве значения
Для чего служит ключевое слово «yield»?
output: sun mon
Ключевое слово yield может обратить любую функцию в генератор. Оно работает подобно оператору return, с той разницей, что ключевое слово будет возвращать объект-генератор. Также функция может совершать несколько вызовов ключевого слова yield.
def testgen(index): weekdays = ['sun','mon','tue','wed','thu','fri','sat'] yield weekdays[index] yield weekdays[index+1]
day = testgen(0) print next(day), next(day)
Что такое __init__.py? Как импортировать класс из другого каталога?
__init__.py в основном используется для инициализации пакетов Python. Файл __init__.py в каталоге lstm_m указывает интерпретатору Python, что этот каталог должен обрабатываться как пакет Python.
Обычно __init__.py является пустым файлом. А если нам нужно использовать lstm.py в файле run.py, то его нужно импортировать следующим образом:
from lstm_m import lstm
Кроме того, внутри папки модуля должен быть файл __init__.py, предназначенный для импорта.
Что такое docstring в Python?
Строка документации в Python (docstring) — способ документирования функций, модулей и классов. """This is onliner docstring""" """ This is multiline """
Как можно конвертировать число в строку?
Для преобразования числа в строку, как правило, используют встроенную функцию str(), хотя есть и другие способы, такие как “{0:d}”.format(число) и “%d”%число. Если вы хотите преобразовать десятичное число в восьмеричное (oct — octal) или шестнадцатеричное (hex — hexadecimal), используйте встроенную функцию oct() или hex() соответственно.
В чём разница между Xrange и range?
Функция xrange() возвращает объект xrange, в то время как range() возвращает список и использует то же количество памяти, независимо от размера функции.
xrange() не создает статический список, как функция range(). Вместо этого он больше похож на генератор, который выдает значения по запросу.
Функция range() получает все числа сразу перед выполнением любой инструкции в цикле.
Как увидеть методы или атрибуты объекта?
Команда dir(x) возвращает отсортированный список имен атрибутов для любого переданного в нее объекта. Если ни один объект не указан, dir() возвращает имена в текущей области видимости.
Перечисление через enumerate
for i, item in enumerate(iterable):
print i, item
> > > list(enumerate(‘abc’))
[(0, ‘a’), (1, ‘b’), (2, ‘c’)]
> > > list(enumerate(‘abc’, 1))
[(1, ‘a’), (2, ‘b’), (3, ‘c’)]
dict/set comprehension
my_dict = {i: i * i for i in xrange(100)} my_set = {i * 15 for i in xrange(100)}
класс итератора
class SimpleIterator: def \_\_iter\_\_(self): return self
def \_\_init\_\_(self, limit): self. limit = limit self. counter = 0
def \_\_next\_\_(self): if self.counter < self.limit: self.counter += 1 return 1 else: raise StopIteration
дескрипторы
С помощью дескрипторов в Python реализована практически вся магия при работе с объектами, классами и методами. Чтобы определить свой собственный дескриптор, нужно определить класс. методы __get__, __set__ или __delete__. После этого мы можем создать какой-то новый класс и в атрибут этого класса записать объект типа дескриптор. Таким образом, наш атрибут будет являться дескриптором. У него будет переопределено поведение при доступе к атрибуту, при присваивании значений или при удалении. Метод __get__ определяет поведение при доступе к атрибуту. Метод __set__ будет переопределять какое-то поведение, если мы попытаемся в наш атрибут что-то присвоить, а метод __delete__ будет говорить о том, что будет происходить, если мы удалим наш атрибут.
Можно переопределить любой из трех методов, и класс уже будет являться дескриптором. Если у вас переопределен только метод __get__, то это non-data дескриптор, если __set__ или delete — то это data дескриптор. Это говорит о том, в каком порядке они будут искаться, вызываться при поиске атрибутов.
WSGI
WSGI — стандарт взаимодействия между Python-программой, выполняющейся на стороне сервера, и самим веб-сервером, например Apache.
WSGI предоставляет простой и универсальный интерфейс между большинством веб-серверов и веб-приложениями или фреймворками.
По стандарту, WSGI-приложение должно удовлетворять следующим требованиям:
должно быть вызываемым (callable) объектом (обычно это функция или метод)
принимать два параметра:
словарь переменных окружения (environ)
обработчик запроса (start_response)
вызывать обработчик запроса с кодом HTTP-ответа и HTTP-заголовками
возвращать итерируемый объект с телом ответа
Чтобы запустить наше WSGI приложение, нужен WSGI сервер. Он запускает WSGI приложение один раз при каждом HTTP запросе от клиента.
Задачи WSGI сервера:
Сформировать переменные окружения (environment)
Описать функцию обработчик запроса (start_response)
Передать их в WSGI приложение
Результат WSGI сервер отправляет по HTTP клиенту
а WSGI шлюз приводит к формату клиент-серверного протокола (CGI, FastCGI, SCGI, uWSGI, …) и передает их на Веб-сервер (например выводит в stdout, stderr).
Функция start_response принимает два обязательных аргумента:
status - строка содержащая статус HTTP ответа, например 200 OK.
response_headers - список кортежей, которые содержат заголовки ответа, например [(‘Content-Type’, ‘text/html’), (‘Content-Length’, ‘15’).
start_response возвращает вызываемый объект, обычно «write». write выводит тело ответа в поток вывода, используется при необычных обстоятельствах. Обратный вызов write плохо поддерживается серверами и веб-фреймворками, поэтому рекомендуется проектировать свои приложения без его вызова.
Помимо приложений и серверов, стандарт дает определение middleware-компонентов, предоставляющих интерфейсы как приложению, так и серверу. То есть для сервера middleware является приложением, а для приложения сервером. Это позволяет составлять «цепочки» WSGI-совместимых middleware.
Middleware могут брать на себя следующие функции (но не ограничиваются этим):
обработка сессий аутентификация/авторизация управление URL (маршрутизация запросов) балансировка нагрузки пост-обработка выходных данных (например, проверка на валидность)
GIL
Global Interpreter Lock (GIL) — это способ синхронизации потоков, который используется в некоторых интерпретируемых языках программирования, например в Python и Ruby.
GIL является самым простым способом избежать конфликтов при одновременном обращении разных потоков к одним и тем же участкам памяти[1]. Когда один поток захватывает его, GIL, работая по принципу мьютекса, блокирует остальные. Нет параллельных потоков — нет конфликтов при обращении к разделяемым объектам. Очерёдность выполнения потоков определяет интерпретатор в зависимости от реализации, переключение между потоками может происходить: когда активный поток пытается осуществить ввод-вывод, по исчерпании лимита выполненных инструкций либо по таймеру
Главный недостаток подхода обеспечения потокобезопасности при помощи GIL — это ограничение параллельности вычислений. GIL не позволяет достигать наибольшей эффективности вычислений при работе на многоядерных и мультипроцессорных системах[3]. Также использование нескольких потоков накладывает издержки на их переключение из-за эффекта конкуренции (потоки «пытаются» перехватить GIL). То есть многопоточное выполнение может занять большее время, чем последовательное выполнение тех же задач[4].
Причины использования GIL:
Однопоточные сценарии выполняются значительно быстрее, чем при использовании других подходов обеспечения потокобезопасности;
Простая интеграция библиотек на C, которые зачастую тоже не потокобезопасны;
Простота реализации.
GIL используется в CPython’е, наиболее распространённой реализации интерпретатора языка Python
Реализация классов
class SomeClass(object): # поля и методы класса SomeClass Классы-родители перечисляются в скобках через запятую:
class SomeClass(ParentClass1, ParentClass2, …): # поля и методы класса SomeClass
class SomeClass(object): attr1 = 42 attr2 = "Hello, World" def method1(self, x): # код метода self – общепринятое имя для ссылки на объект, в контексте которого вызывается метод. Этот параметр обязателен и отличает метод класса от обычной функции.
class SomeClass(object): attr1 = 42
def method1(self, x): return 2*x
obj = SomeClass()
obj. method1(6) # 12
obj. attr1 # 42
Можно создавать разные инстансы одного класса с заранее заданными параметрами с помощью инициализатора (специальный метод __init__). Для примера возьмем класс Point (точка пространства), объекты которого должны иметь определенные координаты:
class Point(object): def \_\_init\_\_(self, x, y, z): self.coord = (x, y, z)
p = Point(13, 14, 15)
p.coord # (13, 14, 15)
Статические и классовые методы
Для создания статических методов в Python предназначен декоратор @staticmethod. У них нет обязательных параметров-ссылок вроде self. Доступ к таким методам можно получить как из экземпляра класса, так и из самого класса:
class SomeClass(object): @staticmethod def hello(): print("Hello, world")
SomeClass.hello() # Hello, world
obj = SomeClass()
obj.hello() # Hello, world
Еще есть так называемые методы классов. Они аналогичны методам экземпляров, но выполняются не в контексте объекта, а в контексте самого класса (классы – это тоже объекты). Такие методы создаются с помощью декоратора @classmethod и требуют обязательную ссылку на класс (cls). class SomeClass(object): @classmethod def hello(cls): print('Hello, класс {}'.format(cls.\_\_name\_\_))
SomeClass.hello() # Hello, класс SomeClass
Жизненный цикл объекта
С инициализатором объектов __init__ вы уже знакомы. Кроме него есть еще и метод __new__, который непосредственно создает новый экземпляр класса. Первым параметром он принимает ссылку на сам класс:
class SomeClass(object): def \_\_new\_\_(cls): print("new") return super(SomeClass, cls).\_\_new\_\_(cls)
def \_\_init\_\_(self): print("init")
obj = SomeClass(); # new # init
Метод __new__ может быть очень полезен для решения ряда задач, например, создания иммутабельных объектов или реализации паттерна Синглтон:
class Singleton(object): obj = None # единственный экземпляр класса
def \_\_new\_\_(cls, *args, **kwargs): if cls.obj is None: cls.obj = object.\_\_new\_\_(cls, *args, **kwargs) return cls.obj
single = Singleton() single.attr = 42 newSingle = Singleton() newSingle.attr # 42 newSingle is single # true
Деструктор
В Python вы можете поучаствовать не только в создании объекта, но и в его удалении. Специально для этого предназначен метод-деструктор __del__.
class SomeClass(object): def \_\_init\_\_(self, name): self.name = name
def \_\_del\_\_(self): print('удаляется объект {} класса SomeClass'.format(self.name))
obj = SomeClass(“John”);
del obj # удаляется объект John класса SomeClass
На практике деструктор используется редко, в основном для тех ресурсов, которые требуют явного освобождения памяти при удалении объекта. Не следует совершать в нем сложные вычисления.
Объект как функция
Объект класса может имитировать стандартную функцию, то есть при желании его можно “вызвать” с параметрами. За эту возможность отвечает специальный метод __call__:
class Multiplier: def \_\_call\_\_(self, x, y): return x*y
multiply = Multiplier()
multiply(19, 19) # 361
# то же самое
multiply.__call__(19, 19) # 361
Объект как коллекция значений
Можно работать с объектом как с коллекцией значений, определив для него интерфейс классического списка с помощью специальных методов:
__getItem__ – реализация синтаксиса obj[key], получение значения по ключу;
__setItem__ – установка значения для ключа;
__delItem__ – удаление значения;
__contains__ – проверка наличия значения.
Реализация инкапсуляции
Все объекты в Python инкапсулируют внутри себя данные и методы работы с ними, предоставляя публичные интерфейсы для взаимодействия.
Атрибут может быть объявлен приватным (внутренним) с помощью нижнего подчеркивания перед именем, но настоящего скрытия на самом деле не происходит – все на уровне соглашений.
class SomeClass: def _private(self): print("Это внутренний метод объекта")
obj = SomeClass()
obj._private() # это внутренний метод объекта
Если поставить перед именем атрибута два подчеркивания, к нему нельзя будет обратиться напрямую. Но все равно остается обходной путь:
class SomeClass(): def \_\_init\_\_(self): self.\_\_param = 42 # защищенный атрибут
obj = SomeClass()
obj. __param # AttributeError: ‘SomeClass’ object has no attribute ‘__param’
obj. _SomeClass__param # 42
Кроме прямого доступа к атрибутам (obj.attrName), могут быть использованы специальные методы доступа (геттеры, сеттеры и деструкторы):
class SomeClass(): def \_\_init\_\_(self, value): self._value = value
def getvalue(self): # получение значения атрибута return self._value
def setvalue(self, value): # установка значения атрибута self._value = value
def delvalue(self): # удаление атрибута del self._value
value = property(getvalue, setvalue, delvalue, "Свойство value")
obj = SomeClass(42)
print(obj.value)
obj.value = 43
Такой подход очень удобен, если получение или установка значения атрибута требует сложной логики.
Вместо того чтобы вручную создавать геттеры и сеттеры для каждого атрибута, можно перегрузить встроенные методы __getattr__, __setattr__ и __delattr__. Например, так можно перехватить обращение к свойствам и методам, которых в объекте не существует:
class SomeClass(): attr1 = 42
def \_\_getattr\_\_(self, attr): return attr.upper()
obj = SomeClass()
obj.attr1 # 42
obj.attr2 # ATTR2
__getattribute__ перехватывает все обращения (в том числе и к существующим атрибутам):
class SomeClass(): attr1 = 42
def \_\_getattribute\_\_(self, attr): return attr.upper()
obj = SomeClass()
obj.attr1 # ATTR1
obj.attr2 # ATTR2
Таким образом, можно организовать динамический доступ к методам и свойствам объекта, как действуют, например, RPC-системы.
Реализация наследования
Язык программирования Python реализует как стандартное одиночное наследование:
class Mammal(): className = 'Mammal'
class Dog(Mammal): species = 'Canis lupus'
dog = Dog()
dog.className # Mammal
так и множественное:
class Horse(): isHorse = True
class Donkey(): def isDonkey = True
class Mule(Horse, Donkey):
mule = Mule()
mule.isHorse # True
mule.isDonkey # True
Ассоциация классов
один класс является полем другого
Пример композиции:
class Salary: def \_\_init\_\_(self,pay): self.pay = pay
def getTotal(self): return (self.pay*12)
class Employee: def \_\_init\_\_(self,pay,bonus): self.pay = pay self.bonus = bonus self.salary = Salary(self.pay)
def annualSalary(self): return "Total: " + str(self.salary.getTotal() + self.bonus) employee = Employee(100,10) print(employee.annualSalary())
Пример агрегации: class Salary(object): def \_\_init\_\_(self, pay): self.pay = pay
def getTotal(self): return (self.pay * 12)
class Employee(object): def \_\_init\_\_(self, pay, bonus): self.pay = pay self.bonus = bonus
def annualSalary(self): return "Total: " + str(self.pay.getTotal() + self.bonus)
salary = Salary(100)
employee = Employee(salary, 10)
print(employee.annualSalary())
Ассоциированные объекты могут циклически ссылаться друг на друга, что ломает стандартный механизм сборки мусора. Избежать подобных проблем при ассоциации помогают слабые ссылки (модуль weakref).
Реализация полиморфизма
class Mammal: def move(self): print('Двигается')
class Hare(Mammal): def move(self): print('Прыгает')
animal = Mammal()
animal.move() # Двигается
hare = Hare()
hare.move() # Прыгает
Впрочем, можно получить и доступ к методам класса-предка либо по прямому обращению, либо с помощью функции super:
class Parent(): def \_\_init\_\_(self): print('Parent init')
def method(self): print('Parent method')
class Child(Parent): def \_\_init\_\_(self): Parent.\_\_init\_\_(self)
def method(self): super(Child, self).method()
child = Child() # Parent init
child.
method() # Parent method
Порядок разрешения доступа к атрибутам
Складывается достаточно интересная картина: у одного объекта может быть несколько родительских классов, а также специальные методы вроде __getattribute__, которые перехватывают запросы к атрибутам.
Каким же образом интерпретатор разрешает сложные запросы к свойствам и методам? Рассмотрим последовательность поиска на примере запроса obj.field:
Вызов obj.__getattribute__(‘field’), если он определен. При установке или удалении атрибута проверяется соответственно наличие __setattr__ или __delattr__.
Поиск в obj.__dict__ (пользовательские атрибуты).
Поиск в object.__class__.__slots__.
Рекурсивный поиск в поле __dict__ всех родительских классов. Если класс имеет несколько предков, порядок проверки соответствует порядку их перечисления в определении.
Если определен метод __getattr__, то происходит вызов obj.__getattr__(‘field’)
Выбрасывается исключение несуществующего атрибута – AttributeError.
Наконец, когда атрибут нашелся, проверяется наличие метода __get__ (при установке – __set__, при удалении – __delete__).
Все эти проверки совершаются только для пользовательских атрибутов.
Асинхронная концепция
В каждой программе строки кода выполняются поочередно. Например, если у вас есть строка кода, которая запрашивает что-либо с сервера, то это означает, что ваша программа не делает ничего во время ожидания ответа. В некоторых случаях это допустимо, но во многих — нет. Одним из решений этой проблемы являются потоки (threads).
Потоки дают возможность вашей программе выполнять ряд задач одновременно. Конечно, у потоков есть ряд недостатков. Многопоточные программы являются более сложными и, как правило, более подвержены ошибкам. Они включают в себя такие проблемы: состояние гонки (race condition), взаимная (deadlock) и активная (livelock) блокировка, исчерпание ресурсов (resource starvation).
Хотя асинхронное программирование и позволяет обойти проблемные места потоков, оно было разработано для совершенно другой цели — для переключения контекста процессора. Когда у вас есть несколько потоков, каждое ядро процессора может запускать только один поток за раз. Для того, чтобы все потоки/процессы могли совместно использовать ресурсы, процессор очень часто переключает контекст. Чтобы упростить работу, процессор с произвольной периодичностью сохраняет всю контекстную информацию потока и переключается на другой поток.
Асинхронное программирование — это потоковая обработка программного обеспечения / пользовательского пространства, где приложение, а не процессор, управляет потоками и переключением контекста. В асинхронном программировании контекст переключается только в заданных точках переключения, а не с периодичностью, определенной CPU.
Зеленые потоки (green threads) являются примитивным уровнем асинхронного программирования. Зеленый поток — это обычный поток, за исключением того, что переключения между потоками производятся в коде приложения, а не в процессоре. Gevent — известная Python-библиотека для использования зеленых потоков. Gevent — это зеленые потоки и сетевая библиотека неблокирующего ввода-вывода Eventlet. Gevent.monkey изменяет поведение стандартных библиотек Python таким образом, что они позволяют выполнять неблокирующие операции ввода-вывода.
Цикл событий — это очередь событий/заданий и цикл, который вытягивает задания из очереди и запускает их. Эти задания называются сопрограммами. Они представляют собой небольшой набор команд, содержащих, помимо прочего, инструкции о том, какие события при необходимости нужно возвращать в очередь.
Обратный вызов — это функция, которая означает: «Как только это будет сделано, выполните эту функцию». Другими словами, вы звоните в службу поддержки и оставляете свой номер, чтобы они, когда будут доступны, перезвонили, вместо того, чтобы ждать их ответа.
любые исключения, созданные в функции обратного вызова, прерывают цикл событий и останавливают выполнение программы. Поэтому все ошибки должны быть переданы как объекты, а не обработаны в виде исключений. Это означает, что если вы не проверили наличие ошибок, то они не будут обрабатываться.
Другая проблема с обратными вызовами заключается в том, что в асинхронном программировании единственный способ избегать блокировок — это обратный вызов. Это может привести к очень длинной цепочке: обратный вызов после обратного вызова после обратного вызова. Поскольку теряется доступ к стеку и переменным, вы в конечном итоге переносите большие объекты во все ваши обратные вызовы, но если вы используете сторонние API-интерфейсы, то не можете передать что-либо в обратный вызов, если он этого не может принять. Это также становится проблемой, потому что каждый обратный вызов действует как поток.
Зеленые потоки
потоки управляются на уровне приложений, а не аппаратно;
включают в себя все проблемы потокового программирования.
Обратный вызов
сопрограммы невидимы для программиста;
обратные вызовы ограничивают использование исключений;
обратные вызовы трудно отлаживаются.
Библиотека Asyncio довольно мощная, поэтому Python решил сделать ее стандартной библиотекой. В синтаксис также добавили ключевое слово async. Ключевые слова предназначены для более четкого обозначения асинхронного кода. Поэтому теперь методы не путаются с генераторами. Ключевое слово async идет до def, чтобы показать, что метод является асинхронным. Ключевое слово await показывает, что вы ожидаете завершения сопрограммы.
Как решаются проблемы потоков в asyncio?
- процессорное переключение контекста: Asyncio является асинхронным и использует цикл событий. Он позволяет переключать контекст программно;
- состояние гонки: поскольку Asyncio запускает только одну сопрограмму и переключается только в точках, которые вы определяете, ваш код не подвержен проблеме гонки потоков;
- взаимная/активная блокировка: поскольку теперь нет гонки потоков, то не нужно беспокоиться о блокировках. Хотя взаимная блокировка все еще может возникнуть в ситуации, когда две сопрограммы вызывают друг друга, это настолько маловероятно, что вам придется постараться, чтобы такое случилось;
- исчерпание ресурсов: поскольку сопрограммы запускаются в одном потоке и не требуют дополнительной памяти, становится намного сложнее исчерпать ресурсы. Однако в Asyncio есть пул «исполнителей» (executors), который по сути является пулом потоков. Если запускать слишком много процессов в пуле исполнителей, вы все равно можете столкнуться с нехваткой ресурсов.
обработка входных данных
a, b = map(int, input().split())
import sys
a, b = sys.stdin.read().split()
print(int(a) + int(b))