Как использовать прослушиватели таблиц

Джейк Малфорд

Привет, я Джейк, разработчик программного обеспечения в Deephaven Data Labs в их команде по связям с разработчиками. На протяжении всей своей карьеры я уделял большое внимание расширению возможностей других разработчиков, начиная от работы ассистентом в колледже и заканчивая работой над опытом разработчиков в использовании REST API, а теперь работаю с механизмом запросов Deephaven. Это познакомило меня с различными подходами к разработке программного обеспечения. Сегодня я расскажу о том, как недавний опыт привел меня к мысли о программировании, управляемом событиями.

Программирование, управляемое событиями — очень простая концепция. Разработано программное обеспечение, которое запускается после определенных событий. Этими событиями могут быть любые действия пользователя, HTTP-запросы или события из других потоков. Deephaven имеет встроенную поддержку программирования, управляемого событиями, с помощью слушателей таблиц. Слушатели таблиц позволяют вам писать функции, которые выполняются при изменении исходных таблиц.

Простые слушатели

Начнем с простого примера:

from deephaven import listen
from deephaven.TableTools import timeTable
def listener_function(update):
    print(f"FUNCTION LISTENER: update={update}")
table = timeTable("00:00:01").update("X=i").tail(5)
handle = listen(table, listener_function)

Вывод журнала:

FUNCTION LISTENER: update={added={0}, removed={}, modified={}, shifted={}, modifiedColumnSet={}} 
FUNCTION LISTENER: update={added={1}, removed={}, modified={}, shifted={}, modifiedColumnSet={}} 
FUNCTION LISTENER: update={added={2}, removed={}, modified={}, shifted={}, modifiedColumnSet={}}

Этот слушатель просто делает оператор печати с информацией, которая изменилась в таблице. Ничего слишком сложного, правда? Таблица обновляется, и информация печатается.

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

def listener_function(update):
    added_iterator = update.added.iterator()
    while added_iterator.hasNext():
        index = added_iterator.nextLong()
        x = table.getColumnSource("X").get(index)
        
        if (x % 2 == 0):
            print("Even number found")
        else:
            print("Odd number found")

Вывод журнала:

FUNCTION LISTENER: update={added={3}, removed={}, modified={}, shifted={}, modifiedColumnSet={}}

Это, безусловно, работает, но только потому, что наши «события» — это просто добавление чисел. Если бы это была настоящая система, управляемая событиями, у нас могли бы быть разные функции, которые мы хотели бы выполнять в зависимости от события, и это было бы очень сложно упаковать в одну функцию. Вы можете подумать: «Хорошо, так что вместо этого мы можем написать разные функции на основе значения в обновлении таблицы, и наш listener_function может содержать логику для вызова правильной функции». Это может выглядеть так:

def print_odd():
    print("Odd number found")
def print_even():
    print("Even number found")
def listener_function(update):
    added_iterator = update.added.iterator()
    while added_iterator.hasNext():
        index = added_iterator.nextLong()
        x = table.getColumnSource("X").get(index)
        
        if (x % 2 == 0):
            print_even()
        else:
            print_odd()

Вывод журнала:

FUNCTION LISTENER: update={added={4}, removed={}, modified={}, shifted={}, modifiedColumnSet={}}

Теперь это больше похоже на традиционную систему, управляемую событиями. Однако с Deephaven вы можете сделать еще один шаг вперед. Для тех, кто не знаком с Deephaven, одной из его основных функций является простота обработки данных в реальном времени. Одним из применений этого является фильтрация данных в режиме реального времени.

Применение слушателей к отфильтрованным таблицам

Вместо того, чтобы содержать условную логику в listener_function, мы можем использовать фильтры Deephaven, чтобы извлечь эту логику и создать две таблицы (одну для нечетных чисел, а другую для четных чисел), а затем зарегистрировать наши функции print_odd и print_even в качестве функций слушателя для этих столы.

from deephaven import listen
from deephaven.TableTools import timeTable
def print_odd(update): #Even though we aren't using the update parameter, it is still required to be a listener
    print("Odd number found")
def print_even(update):
    print("Even number found")
table = timeTable("00:00:01").update("X=i").tail(5)
evens = table.where("X % 2 == 0")
odds = table.where("X % 2 == 1")
even_handle = listen(evens, print_even)
odd_handle = listen(odds, print_odd)

Вывод журнала:

FUNCTION LISTENER: update={added={7}, removed={2}, modified={}, shifted={}, modifiedColumnSet={}} Even number found 
FUNCTION LISTENER: update={added={8}, removed={3}, modified={}, shifted={}, modifiedColumnSet={}} Odd number found

Этот подход имеет несколько преимуществ. Нам больше не нужен общий listener_function, содержащий условную логику; эта логика была перенесена в методы фильтрации Deephaven. Это позволяет нам использовать более простые функции слушателя для наших таблиц.

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

  • создать новую таблицу на основе фильтра нашей существующей таблицы,
  • создать новую функцию слушателя,
  • и зарегистрируйте эту функцию прослушивателя в таблице.
def print_threes(update):
    print("Multiple of 3 found")
threes = table.where("X % 3 == 0")
threes_handler = listen(threes, print_threes)

Вывод журнала:

FUNCTION LISTENER: update={added={9}, removed={4}, modified={}, shifted={}, modifiedColumnSet={}} Even number found 
FUNCTION LISTENER: update={added={10}, removed={5}, modified={}, shifted={}, modifiedColumnSet={}} 
Odd number found 
Multiple of 3 found

Цепочка условных операторов также проще. Допустим, у нас есть особый случай, и нам нужен дополнительный оператор печати для нечетных чисел, кратных 5. С условными операторами для этого нам понадобится вложенный оператор if-else. Но с Deephaven мы можем создать новую таблицу нечетных чисел, кратных 5, на основе нашей существующей таблицы нечетных чисел и создать прослушиватель для этой таблицы.

def print_odd_fives(update):
    print("Odd multiple of 5 found")
odd_fives = odds.where("X % 5 == 0")
odd_fives_handler = listen(odd_fives, print_odd_fives)

Вывод журнала:

FUNCTION LISTENER: update={added={11}, removed={6}, modified={}, shifted={}, modifiedColumnSet={}} 
Even number found 
FUNCTION LISTENER: update={added={12}, removed={7}, modified={}, shifted={}, modifiedColumnSet={}} 
Even number found 
Odd number found 
Multiple of 3 found 
Odd multiple of 5 found 
FUNCTION LISTENER: update={added={13}, removed={8}, modified={}, shifted={}, modifiedColumnSet={}} 
Even number found 
Odd number found 
Multiple of 3 found

Слушатели Deephaven чрезвычайно эффективны, и знакомство с ними вместе с языком запросов Deephaven было отличным для разработки другого взгляда на проблемы и их решения.

Загляните к нам на нашу домашнюю страницу https://deepphaven.io/, и я приглашаю вас присоединиться к нам в Slack!

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.