Декораторы в Python

Вероятно, почти каждый разработчик на Python сталкивался с декораторами, видя конструкцию с со знаком @:

@app.route('/')
def index():
    return "Hello, World!"

Разберемся, что такое декоратор, и как он работает. Этот вопрос часто спрашивают на собеседованиях.

Декоратор – это функция, которая принимает как аргумент другую функцию*. Цель декоратора – расширить функциональность переданной ему функции без непосредственного изменения кода самой функции. Вот и все!

* Примечание: декорировать можно и класс, но об этом расскажу потом!

В Python функция – тоже объект, и ее можно передавать как аргумент, возвращать из другой функции, ей также можно назначать атрибуты.

Символ собачка (@) – всего лишь синтаксический сахар:

@decorator
def foo():
    ...

# эквивалентно:

def foo():
    ...
foo = decorator(foo)

ЧИТАТЬ ДАЛЬШЕ, КАК СОЗДАТЬ ДЕКОРАТОР…

Куча и очередь с приоритетом

Порядок обхода кучи

Очередь с приоритетом – это такая коллекция, которая поддерживает обязательно две следующие операции: вставка элемента с некоторым приоритетом (это может быть число или другой сравнимый объект) и извлечение элемента с наибольшим (или наименьшим приоритетом).

ЧИТАТЬ ПОДРОБНЕЕ

Перенаправление стандартного вывода

Случается так, что некий код (возможно, не ваш) пишет в стандартный вывод какую-то нужную информацию. Ее нетрудно перехватить с помощью функции redirect_stdout из стандартного модуля contextlib.

redirect_stdout является контекст менеджером (применяется совместно с with) и принимает аргументом файло-подобный объект (это может быть и дескриптор файла, и StringIO). 

📎 Пример. Сохраним вывод функции help в строку (в интерпретаторе пример работает некорректно, запускайте с файла):

import io
from contextlib import redirect_stdout

f = io.StringIO()
with redirect_stdout(f):
  help(pow)
s = f.getvalue() 
print(s) # в s будет вывод

📎 Пример. Или в файл:

with open('help.txt', 'w') as help_file:
  with redirect_stdout(help_file):
    help(pow)   

📎 Пример. stdout в stderr:

import sys
with redirect_stdout(sys.stderr):
  help(pow)

Во время работы redirect_stdout вывод в терминал попадать не будет.

Функция redirect_stderr аналогично перехватывает вывод из стандартного потока ошибок (stderr).

🧙 Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈 

Многоликий else

Все знают, что ключевое слово else служит для выполнения альтернативной ветки кода, если условие if не выполнилось:

x = 5
if x < 3:
    print("x < 3")
else:
    print("x >= 3")

Но знали ли вы, что есть еще два примения else?

1. for/else, while/else

Если поставить else после тела цикла, то код по else будет выполнен только в том случае, если цикл завершился «нормально», т.е. в цикле не исполнилось break. Пример:

stack = [1, 3, 5, 7]
while stack:
    if stack.pop() == 4:
        break
else:
    print('not found!')

Пример. Простые числа и множители:

for n in range(2, 10):
    for x in range(2, n):
        if n % x == 0:
            # есть делитель, уходим
            print(n, '=', x, '*', n/x)
            break
    else:
        # цикл не нашел делителей
        print(n, 'простое число!')
2. try/else

В блоке try код else выполняется только в том случае, если не возникло исключений. else можно написать только после блока except, без него – нельзя. Порядок выполнения кода соответствует порядку написания сверху вниз: tryexcept или elsefinally.

try:
    ...
except Exception:
    print('Exception!')
else:
    print('Ok!')
finally:
    print('Bye!')

Причем else не будет также вызван, если сработавшее исключение не подпало под перечисленные except и не было обработано.

🧙 Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈 

⁠🎚️Интерактивность в Jupyter Notebook

Как я и говорил, в Jupyter Notebook очень много всяких удобностей. В частности в блокнот можно добавить элементы управления, такие как:

  • Слайдер для выбора значения числа
  • Текстовое поле для ввода чисел или строк
  • Выпадающий список выбора
  • Чекбоксы (галочка да/нет)
  • Выбор даты
  • Выбор цвета и другие…

Установка виджетов (если еще не установлены):

pip install ipywidgets
jupyter nbextension enable --py widgetsnbextension

Или через conda одной командой:

conda install -c conda-forge ipywidgets

📎 Пример. Нарисуем синусоиду с изменяемой частотой и фазой:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

from ipywidgets import interact

@interact(f=(1, 5, 0.1), phase=(0, 3.14, 0.1))
def plot_f(f, phase):
    x = np.linspace(0, 10, 100)
    y = np.sin(f * x + phase)
    plt.plot(x, y, 'r')
    plt.show()

Мы делаем интерактивными параметры f и phase. Их имена совпадают с аргументами обернутой функции plot_f(f, phase). А значения — кортеж вида (min, max, step), т.е. минимальное значение, максимальное и шаг слайдера. Как только мы изменим положение движков, то график будет автоматически перерисован.

Пример интерактивного графика в Jupyter Notebook

Узнать про другие виджеты