Об этом документе

Содержание

Python

Строки

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

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

Можно ли изменить отдельный символ внутри строки?

Нет, строки неизменяемы. Операции замены, форматирования и конкатенации возвращают новую строку.

Как соединить список строк в одну? Как разбить строку на список строк?

Чтобы соединить, нужен метод строки .join(). Чтобы разбить, метод .split().

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

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

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

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

Прокомментировать выражение 'ABC' == u'ABC'

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

Если хотя бы одна буква набрана русским символом, то строки равны не будут. Например, русская буква А имеет код, отличный от латинской буквы A.

Помните, что символы из нижней части таблицы ASCII имеют одни и те же коды в любой кодировке.

Списки, кортежи, словари, множества

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

На уровне языка отличаются тем, что в кортеж нельзя добавить или убрать элемент. На уровне интерпретатора различий нет. Обе коллекции представлены массивом указателей на структуру PyObject.

Для списка определены функции, которые добавляют в такой массив новый элемент, удаляют имеющийся, соединяют два массива в один. Они вызываются методами списка .append(), .pop(), .sort() и т.д.

Как сделать список уникальным (без повторяющихся элементов)?

  • Вариант со множеством. Не гарантирует порядок элементов. Порядок сохраняется только для маленьких списков.
list(set([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 его значения.

a, b, c = [1, 2, 3]

Как отсортировать список словарей по определенному полю?

Метод списка .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 принимает список пар. Каждую пару он рассматривает как ключ и значение соответственно.

Где быстрее поиск элемента -— в списке или множестве?

Во множестве, потому что множество работает как словарь. Значение ищется по хешу ключа. Вычисление хеша и сопоставление адреса – операции постоянной сложности, поэтому принято говорить, что поиск в словаре равен O(1).

Исключение работает только для очень маленьких списков длиной до 5 элементов. В этом случае интерпретатору будет быстрей пробежаться по списку, чем считать хеш.

Функции

Что такое *args, **kwargs, в каких случаях они требуются?

Выражения *args и **kwargs объявляют в сигнатуре функции. Они означают, что внутри функции будут доступны переменные с именами args и kwargs (без звездочек). Можно использовать другие имена, но это считается дурным тоном.

args – это кортеж, который накапливает позиционные аргументы. kwargs – словарь позиционных аргументов, где ключ – имя параметра, значение – значение параметра.

Важно: если в функцию не передано никаких параметров, переменные будут соответственно равны пустому кортежу и пустому словарю, а не None.

Пожалуйста, не путайте кортеж со списком. Следующий вопрос объясняет, почему.

Почему def foo(bar=[]): плохо? Приведите пример плохого случая. Как исправить?

Функция создается однажды при загрузке модуля. Именованные параметры и их дефолтные значения тоже создаются один раз и хранятся в одном из полей объекта-функции.

В нашем примере 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]

Сказанное выше актуально в т.ч. для множеств и словарей.

Можно ли передавать функцию в качестве аргумента другой функции?

Можно, функция в Питоне объект первого порядка: допускает присваивание, передачу в функцию, удаление.

Можно ли объявлять функцию внутри другой функции? Где она будет видна?

Можно. Такая функция будет видна только внутри первой функции.

Что такое лямбды? Каковы их особенности?

Это анонимные функции. Они не резервируют имени в пространстве имен. Лямбды часто передают в функции map, reduce, filter.

Лямбды в Питоне могут состоять только из одного выражения. Используя синтаксис скобок, можно оформить тело лямбды в несколько строк.

Использовать точку с запятой для разделения операторов нельзя.

Допустимы ли следующие выражения?

  • nope = lambda: pass
  • riser = lambda x: raise Exception(x)

Нет, при загрузке модуля выскочит исключение SyntaxError. В теле лямбды может быть только выражение. pass и raise являются операторами.

Декораторы

Что такое декораторы? Зачем нужны?

Декоратор в широком смысле – паттерн проектирования, когда один объект изменяет поведение другого. В Питоне декоратор, как правило, это функция A, которая принимает функцию B и возвращает функцию C. При этом функция C задействует в себе функцию B.

Задекорировать функцию значит заменить ее на результат работы декоратора.

Что может быть декоратором? К чему может быть применен декоратор?

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

Применять декоратор можно к любому объекту. Чаще всего к функциям, методам и классам. Декорирование встречается настолько часто, что под него выделен особый оператор @.

def auth_only(view):
    ...

@auth_only
def dashboard(request):
    ...

Если бы оператора декорирования не существовало, мы бы записали код выше так:

def auth_only(view):
    ...

def dashboard(request):
    ...

dashboard = auth_only(dashboard)

Что будет, если декоратор не возвращает ничего?

Если в теле функции нет оператора return, вызов вернет None. Помним, результат декоратора замещает декорируемый объект. В нашем случае декоратор вернет None и функция, которую мы декорируем, тоже станет None. При попытке вызвать ее после декорирования получим ошибку NoneType is not callable.

В чем отличие @foobar от @foobar()?

Первое – обычное декорирование функцией 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?

wraps – декоратор из стандартной поставки Питона, модуль functools. Он назначает функции-врапперу те же поля __name__, __module__, __doc__, что и у исходной функции, которую вы декорируете. Это нужно для того, чтобы после декорирования функция-враппер не выглядела в стектрейсах как исходная функция.

Итерация и генераторы

В чем отличие [x for x in y] от (x for x in y)?

Первое выражение возвращает список, второе – генератор.

Что особенного в генераторе?

Генератор хранит в памяти не все элементы, а только внутреннее состояние для вычисления очередного элемента. На каждом шаге можно вычислить только следующий элемент, но не предыдущий. Пройти генератор в цикле можно только один раз.

Как объявить генератор?

  • использовать синтаксис (x for x in seq)
  • оператор yield в теле функции вместо return
  • встроенная функция iter, которая вызывает у объекта метод __iter__(). Этот метод должен возвращать генератор.

Как получить из генератора список?

Передать его в конструктор списка: list(x for x in some_seq). Важно, что после этого по генератору уже нельзя будет итерироваться.

Можно ли извлечь элемент генератора по индексу?

Нет, будет ошибка. Генератор не поддерживает метод __getitem__.

Что возвращает итерация по словарю?

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

for key in {'foo': 1, 'bar': 2}:
    process_key(key)

Как итерировать словарь по парам ключ-значение?

Метод словаря .iteritems() возвращает генератор кортежей (key, value).

Классы, объекты

Как получить список атрибутов объекта?

Функция dir возвращает список строк – полей объекта. Поле __dict__ содержит словарь вида {поле -> значение}.

Что такое магические методы, для чего нужны?

Магическими метода называют методы, имена которых начинаются и заканчиваются двойным подчеркиванием. Магические они потому, что почти никогда не вызываются явно. Их вызывают встроенные функции или синтаксические конструкции. Например, функция len() вызывает метод __len__() переданного объекта. Метод __add__(self, other) вызывается автоматически при сложении оператором +.

Перечислим некоторые магические методы:

  • __init__: конструктор класса
  • __add__: сложение с другим объектом
  • __eq__: проверка на равенство с другим объектом
  • __cmp__: сравнение (больше, меньше, равно)
  • __iter__: при подстановке объекта в цикл

Как в классе сослаться на родительский класс?

Функция super принимает класс и экземпляр:

class NextClass(FirstClass):
    def __init__(self, x):
        super(NextClass, self).__init__()
        self.x = x

Возможно ли множественное наследование? Что такое MRO?

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

MRO – method resolution order, порядок разрешения методов. Алгоритм, по которому следует искать метод в случае, если у класса два и более родителей. Алгоритм линеизирует граф наследования. Коротко можно описать так: ищи слева направо. Поэтому чем правее стоит класс, тем меньше у него приоритет при поиске метода.

Что такое миксины?

Миксин (mix-in, анг. “примесь”), паттерн проектирования в ООП, когда в цепочку наследования добавляется небольшой класс-помощник. Например, есть класс

class NowMixin(object):
    def now():
        return datetime.datetime.utcnow()

Тогда любой класс, наследованный с этим миксином, будет иметь метод now().

Что такое контекстный менеджер? Как написать свой?

В питоне есть оператор with. Размещенный внутри код выполняется с особенностью: до и после гарантированно срабатывают события входа в блок withи выхода из него. Объект, который определяет логику событий, называется контекстным менеджером.

На уровне класса события определены методами __enter__ и __exit__. Первый срабатывает в тот момент, когда ход исполнения программы переходит внутрь with. Метод может вернуть значение. Оно будет доступно низлежащему внутри блока with коду.

__exit__ срабатывает в момент выхода блока, в т.ч. и по причине исключения. В этом случае в метод будет передана тройка значений (exc_class, exc_instance, traceback).

Самый распространённый контекстный менеджер – класс, порожденный функцией open. Он гарантирует, что файл будет закрыт даже в том случае, если внутри блока возникнет ошибка.

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

with open('file.txt') as f:
    data = f.read()
process_data(data)

В примере выше мы вышли из блока with сразу же после прочтения файла. Обработка данных происходит в основном блоке программы.

Контекстные менеджеры можно использовать для временной замены параметров, переменных окружения, транзакций БД.

Прокомментировать выражение object() == object()

Всегда ложь, поскольку по умолчанию объекты сравниваются по полю id (адрес в памяти), если только не переопределен метод __eq__.

  • Как удаляется объект?

Что такое __slots__? Плюсы, минусы.

Классы хранят поля и их значения в секретном словаре dict. Поскольку словарь – изменяемая структура, вы можете на лету добавлять и удалять из класса поля. Параметр slots в классе жестко фиксирует набор полей класса. Слоты используются когда у класса может быть очень много полей, например, в некоторых ORM, либо когда критична производительность, потому что доступ к слоту срабатывает быстрее, чем поиск в словаре.

Слоты активно используются в библиотеках requests и falcon.

Недостатки: нельзя присвоить классу поле, которого нет в слотах. Не работают методы __getattr__ и __setattr__.

В чем смысл параметров_value, __value

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

class Foo(object):
    def __init__(self):
        self._bar = 42

Foo()._bar
>>> 42

Современные IDE вроде PyCharm подсвечивают обращение к полю с подчеркиванием, но ошибки в процессе исполнения не будет.

Поля с двойным подчеркиванием доступны внутри класса, но недоступны извне. Это достигается хитрым приемом: интерпретатор назначает таким полям имена вида _<ClassName>__<fieldName>. Зная это правило, можно получить значение скрытого поля вне класса, но это смотрится очень уродливо.

class Foo(object):
    def __init__(self):
        self.__bar = 42

Foo().__bar
>>> AttributeError: 'Foo' object has no attribute '__bar'
Foo()._Foo__bar
>>> 42

Многопоточность

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

Многопоточность достигается модулем Threading. Это нативные Posix-треды. Такие треды исполняются операционной системой, а не виртуальной машиной.

Что такое GIL? Как работает? Какие проблемы?

Global Interpreter Lock. Особенность интерпретатора, когда одновременно может исполняться только один тред. Все это время остальные треды простаивают. GIL есть не только в Питоне, но и в других скриптовых языках, например, Руби.

Причина существования GIL в том, что для этих интерпретаторов еще не найден безопасный способ согласовывать изменения данных. Например, если один тред удалит все элемены из списка, а второй начнет итерацию по нему, гарантированно произойдет ошибка.

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

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

В чем отличие тредов от мультипроцессинга?

Главное отличие в разделении памяти. Процессы независимы друг от друга, имеют раздельные адресные пространства, идентификаторы, ресурсы. Треды исполняются в совместном адресном порстранстве, имеют общий доступ к памяти, переменным, загруженным модулям.

Какие задачи хорошо параллелятся, какие плохо?

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

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

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

Нужно посчитать 100 уравнений. Делать это в тредах или нет?

Нет, потому что в этой задаче нет ввода-вывода. Интерпретатор только будет тратить лишнее время на переключение тредов. Сложные математические задачи лучше выносить в отдельные процессы, либо использовать фреймворк для распределенных задач Celery, либо подключать как C-библиотеки.

Треды в Питоне — это нативные треды или нет?

Да, это нативные Posix-совместимые треды, которые исполняются на уровне операционной системы.

Что такое гринлеты? Общее понятие. Примеры реализаций.

Greenlet == Green thread == Зеленые треды == легковесные треды внутри виртуальной машины. Могут называться корутинами, сопроцессами, акторами и т.д. в зависимости от платформы. Операционная система не видит их. С точки зрения ОС запущен один процесс виртуальной машины, а что внутри нее – неизвестно. Такими тредами управляет сама вируальная машина: порождает, исполняет, согласует доступ к ресурсам.

Примеры: корутины в языках Go и Lua, легковесные процессы в Erlang, модуль greenlet для Python.

Джанго

Что такое Middleware, для чего, как реализуется?

Middleware – особый объект, который изменяет входящий запрос или исходящий ответ. Например, добавляет заголовки, делает предварительные проверки. Middleware нужен, когда требуется подвергнуть обработке все запросы приложения.

На уровне языка это объект с методами process_request и process_response. Методы должны вернуть принятый объект (запрос или ответ) для дальнейшей обработки или выкинуть исключение, если что-то не в порядке. В этом случает дальнейшая обработка прекращается.

Чтобы включить Middleware, достаточно добавить путь к нему в кортеж MIDDLEWARE_CLASSES.

Назовите основные мидлвари? Зачем они нужны?

  • SessionMiddleware – поддержка сессий. Добавляет в запрос объект session
  • CsrfViewMiddleware – проверяет, что POST-запросы отправлены с текущего домена
  • AuthenticationMiddleware – авторизует пользователя. Добавляет в запрос поле user
  • MessageMiddleware – передает пользователю короткие сообщения

  • Опишите алгоритм работы auth middleware, распознавание юзера

Опишите алгоритм работы CSRF middleware

На каждый запрос система генерирует уникальный токен и выставляет его в куках. В каждой форме размещается скрытое поле csrf-token с этим же токеном. При отправке формы методом POST Джанго проверяет, что поле формы и значение в куках совпадают. Если нет, это значит, что запрос подделан или отправлен с другого домена.

Чтобы освободить какую-то вьюху от проверки (если это API, например), достаточно обернуть ее декоратором csrf_except.

Что такое сигналы? Зачем нужны? Назовите основные?

Сигналы – это события в экосистеме Джанго. С помощью сигналов подсистемы оповещают приложение о том, что случилось. Чтобы читать сигналы, программист регистрирет обработчики сигналов. Сигналы распространяются синхронно. Это значит, подписав на один сигнал сотню обработчиков, мы увеличим время, необходимое на отдачу ответа.

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

Важно: сигналы моделей работают поштучно, то есть для одной модели. При пакетной обработке, например, queryset.all().delete() или queryset.all().update({'foo'=42}), события об удалении или изменения не будут вызваны.

  • Как определить свои фильтры для шаблонной системы?
  • Что такое ORM? Как в Джанго организовать связь между моделями?

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

Если есть модели A и B со связью многие ко многим, то создается таблица-мост с именем a_to_b, которая хранит ключ на A, ключ на B и дополнительные сведения, например, время, когда была создана связь. Эта таблица сцепляется с A и B оператором JOIN.

  • Как разворачивали веб-приложения в продакшене?

Чем лучше отправлять форму — GET или POST?

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

Техническое ограничение метода GET в том, что им нельзя передать файл, в отличие от POST.

Форму желательно передавать методом POST по следующим причинам:

  • GET-запросы могут быть кешированы, особенно в браузерах семейства IE
  • GET-запросы оседают в логах провайдера, сервера, истории браузера. Пароль и логин засветиться во многих местах
  • некоторые вирусы отслеживают содержимое адресной строки и пересылают третьим лицам.

Что такое REST?

REST (Representational state transfer) – соглашение о том, как выстраивать сервисы. Под REST часто имеют в виду т.н HTTP REST API. Как правило, это веб-приложение с набором урлов – конечных точек. Урлы принимают и возвращают данные в формате JSON. Тип операции задают методом HTTP-запроса, например:

  • GET – получить объект или список объектов
  • POST – создать объект
  • PUT – обновить существующий объект
  • DELETE – удалить объект
  • HEAD – получить метаданные объекта

REST-архитектура актвивно использует возможности протокола HTTP, чтобы избежать т.н. “велосипедов” – собственных решений. Например, параметры кеширования передаются стандартными заголовками Cache, If-Modified-Since, ETag. Авторизациция – заголовком Authentication.

Что такое XSS? Примеры? Как защитить приложение?

XSS – межсайтовые запросы. Страница, подверженная уязвимости, вынуждает пользователя выполнить запрос к другой странице, либо запустить нежелательный js-код.

Например, пользователь отправил комментарий, в котором был код:

<script>alert('foo');</script>

Движок сайта не фильтрует текст комментария, поэтому тег <script> становится частью страницы и исполняется браузером. Каждый, кто зайдет на страницу с опасным комментарием, увидет всплаывющее окно с тестом foo.

Другой пример. Страница поиска принимает поисковой терм q. В заголовке фраза “Результат поиска по запросу” + текст параметра. Если не экранировать параметр, то запрос /search?q=<script>alert('foo');</script> приведет к аналогичному результату.

Зная, что страница выполняет js-код, хакер может подгрузить на страницу контекстную рекламу, баннеры, заставить браузей перейти на любую страницу, похитить куки.

Уязвимость устраняется экранированием небезопасных символов, чисткой (санацией) HTML-тегов.

Тесты

  • Какие тулзы для тестов?
  • Как тестировать запросы в сеть, базу?

Веб-разработка

Что такое CGI? Плюсы, минусы?

Common Gateway Interface. Соглашение о том, как веб-сервер взаимодействует с программой, написанной на каком-то языке. Веб-сервер запускает программу как исполняемый файл. Параметры запроса, например, метод, путь, заголовки и т.д. передаются через переменные окружения.

Программа должна прочитать эти переменные и записать в стандартный поток вывода HTTP-ответ.

Плюсы:

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

Минусы:

  • Запуск процесса ОС на каждый запрос отрабатывает очень медленно.
  • Передача данных через stdout медленней юникс-сокетов.

Что такое CSRF?

Сross Site Request Forgery (межсайтовая подделка запроса). Вид уязвимости, когда сайт А вынуждает пользователя выполнить запрос на сайт Б. Это может быть тег img или script для GET-запроса, или форма со специальным атрибутом target.

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

Например, пользователь должен заполнить форму. В нее помещают скрытое поле token – одноразовую последовательность символов. Этот же токен сохраняют в куки пользователя. При отправке формы поле и куки должны совпасть. Способ не является надежным и обходится скриптом.

Как защитить куки от воровства и от подделки?

Зависит от того, насколько строгие критерии безопасности на сайте. Если в куках хранятся вспомогательные данные, например, индекс последнего выбранного в дропдауне элемента, правилами ниже можно пренебречь.

Для платежных систем, сайтов с приватными данными приведенные правила обязательны.

  • Выставлять кукам флаг httponly. Браузер не даст прочесть и изменить такие куки на клиенте Джаваскриптом.
  • Использовать флаг secure. Куки будут переданы только по безопасному соединению.
  • Устанавливать короткий срок жизни куки.
  • Устанавливать короткий срок сессии на сервере.
  • Добавлять в ключ сессии заголовок User-Agent. Тогда если украсть куки и установить на другой машине, ключ сессии будет другим.
  • Аналогично пункту выше, но добавлять IP пользователя.
  • Подписывать куки секретным ключом. Добавлять поле sig, которое равно HMAC-SHA1(cookie-body, secret_key). На сервере проверять, что подпись совпадает.

HTTP

Как устроен протокол HTTP?

HTTP – текстовый протокол, работающий поверх TCP/IP. HTTP состоит из запроса и ответа. Их структуры похожи: стартовая строка, заголовки, тело ответа.

Стартовая строка запроса состоит из метода, пути и версии протокола:

GET /index.html HTTP/1.1

Стартовая строка ответа состоит из версии протокола, кода ответа и текстовой расшифровке ответа.

HTTP/1.1 200 OK

Заголовки – это набор пар ключ-значение, например, User-Agent, Content-Type. В заголовках передают метаданные запроса: язык пользователя, авторизацию, перенаправление. Заголовок Host должен быть в запросе всегда.

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

Написать raw запрос главной Яндекса

GET / HTTP/1.1
Host: ya.ru

Как клиенту понять, удался запрос или нет?

Проверить статус ответа. Ответы разделены старшему разряду. Имеем пять групп со следующей семантикой:

  • 1xx: используется крайне редко. В этой группе только один статус 100 Continue.
  • 2xx: запрос прошел успешно (данные получены или созданы)
  • 3xx: перенаправление на другой ресурс
  • 4xx: ошибка по вине пользователя (нет такой страницы, нет прав на доступ)
  • 5xx: ошибка по вине сервера (ошибка в коде, сети, конфигурации)

Что нужно отправить браузеру, чтобы перенаправить на другую страницу?

Минимальный ответ должен иметь статус 301 или 302. Заголовок Location указывает адрес ресурса, на который следует перейти.

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

Как управлять кешированием в HTTP?

Существуют несколько способов кешировать данные на уровне протокола.

  • Заголовки Cache и Cache-Control регулируют сразу несколько критериев кеша: время жизни, политику обновления, поведение прокси-сервера, тип данных (публичные, приватные).
  • Заголовки Last-Modified и If-Modified-Since задают кеширование в зависимости от даты обновления документа.
  • Заголовок Etag кеширует документ по его уникальному хешу.

Как кэшируются файлы на уровне протокола?

Когда Nginx отдает статичный файл, он добавляет заголовок EtagMD5-хеш файла. Клиент запоминает этот хеш. В следующий раз при запросе файла клиент посылает хеш. Сервер проверяет хеш клиента для этого файла. Если хеш не совпадает (файл обновили), сервер отвечает с кодом 200 и выгружает актуальный файл с новым хешем. Если хеши равны, сервер отвечает с кодом 304 Not Modified с пустым телом. В этом случае браузер подставляет локальную копию файла.

СУБД

  • Что делает оператор JOIN, какие виды бывают?
  • Что делает оператор HAVING, примеры?
  • В каких случаях вы бы предпочли нереляционную БД?
  • Что такое SQL-инъекции, какие меры против?
  • Что такое функциональный индекс?
  • Что такое транзакиця, ее свойства?
  • Что такое область видимости транзакции?

Джаваскрипт

  • Отличие == от ===?
  • Что такое замыкания, примеры?
  • Как реализовано ООП, особенности?
  • JSONP - в чем идея, как реализуется
  • На клиенте случилась ошибка, как известить сервер?
  • В чем разница function foo() {...} и var foo = function() {...}
  • Прокомментировать выражение:
(function($) {
    $(function() {
        alert(123);
    });
})(jQuery);
  • Написать простой класс-вектор (x, y) с одним методом.

Деплой

  • Описать схему деплоя типичного приложения. Основные компоненты.

Алгоритмы, структуры

Что такое рекурсия? Какие минусы, плюсы?

Рекурсия – определение функции через саму себя. Логика рекурсивной функции как правило состоит из двух ветвей. Длинная ветвь вызывает эту же функцию с другими параметрами, чтобы накопить результат. Короткая ветвь определяет критерий выхода из рекурсии.

Рекурсия упрощает код и делает его декларативным. Рекурсия поощряет мыслить функционально и избегать побочных эффектов.

Неоптимизированная рекурсия приводит к накладным расходам ресурсов. При большом количестве итераций можно превысить лимит на число рекурсивных вызовов (recursion depth limit reached).

Что такое хвостовая рекурсия?

Это особый вид рекурсии, когда функция заканчивается вызовом самой себя без дополнительных операторов. Когда это условие выполняется, компилятор разворачивает рекурсию в цикл с одним стек-фреймом, просто меняя локальные переменные от итерации к итерации.

Так, классическое определение рекурсивного факториала return N * fact(N - 1) не поддерживает хвостовую рекурсию, потому что для каждого стек-фрейма придется хранить текущее значение N.

Чтобы сделать рекурсии хвостовой, добавляют параметры-аккумуляторы. Благодаря им функция знает о своем текущем состоянии. Пусть параметр acc по умолчанию равен 1. Тогда запись с хвостовой рекурсией будет выглядеть так:

def fact(N, acc=1):
    if N == 1:
        return acc
    else:
        return fact(N - 1, acc * N)

Опишите быструю сортировку. Какова ее сложность?

На первом этапе выбирают опорный элемент. Чаще всего его берут из середины массива. Затем последовательно сравнивают первый элемент массива с последним, второй с предпоследним и т.д. Если элемент слева от опорного элемента больше правого, они меняются местами. Когда доходят до опорного элемента, итерация считается законченной.

Далее описанный выше алгоритм применяют для двух подмассивов. Первый – от первого элемента до опорного элемента (не включительно), второй – от опорного до последнего.

Рекурсивный спуск продолжается, пока длины подмассивов не станут равны единице.

Сложность быстрой сортировки в среднем случае равна N * log(N).

Что такое О-нотация?

todo

Задачи на алгоритмы

  • Как извлечь элементы массива в случайном порядке без повторений?

Практика

  • Написать декоратор, который выводит на экран время работы произвольной функции.
  • Написать декоратор, который возвращает либо результат, либо экземпляр исключения.
  • Написать генератор Фибоначчи от a и b.
  • Получить из файла текст в юникоде.
  • Написать генератор чисел Фибоначчи вида def fib(a=1, b=2):

Задача на ORM

class City(models.Model):
    name = models.CharField()


class Person(models.Model):
    name = models.CharField()
    city = models.ForeignKey(City)
  • Вывести список людей и городов где они живут?
  • Вывести всех людей, живущих в городе N
  • Вывести 5 городов с наибольшим населением, упорядочив по убыванию.

Проектирование

  • Сервис сокращалки урлов