Как работать с интерактивными классами в IPython.parallel?

Контекст

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

Сначала настройте среду параллельной кластеризации:

>>> from IPython.parallel import Client
>>> rc = Client()
>>> lview = rc.load_balanced_view()
>>> rc[:]
<DirectView [0, 1, 2]>

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

>>> class MyClass(object):
...     def __init__(self, parameter):
...         self.parameter = parameter
...
...     def update_something(self, some_data):
...         # do something smart here with some_data & internal state
...
...     def compute_something(self, other_data):
...         # do something smart here with other data & internal state
...         return something
...

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

>>> def process(obj, some_data, other_data):
...     obj.update_something(some_data)
...     return obj.compute_something(other_data)
...
>>> tasks = []
>>> some_instances = [MyClass(i) for i in range(10)]
>>> for obj in some_instances:
...    for some_data in data_source_1:
...         for other_data in data_source_2:
...             ar = lview.apply_async(process, obj, some_data, other_data)
...             tasks.append(ar)
...
>>> # wait for computation to end
>>> results = [ar.get() for ar in tasks] 

Проблема

Очевидно, что это не сработает, так как механизмы представления с балансировкой нагрузки не смогут распаковать экземпляры, переданные в качестве первого аргумента функции process. Само определение функции процесса передается успешно, поскольку я предполагаю, что apply_async выполняет интроспекцию байт-кода для его обработки (путем доступа к атрибуту .code функции), а затем просто выполняет простую обработку оставшихся аргументов.

Возможные решения (которые не работают для меня)

  • Одним из альтернативных решений было бы использование магии ячейки %%px для ячейки, содержащей определение класса MyClass. Однако это помешало бы мне создавать экземпляры классов в клиентском сценарии, которые также выполняют планирование. Мне нужно было бы скопировать и вставить содержимое ячейки в другую ячейку без магии %%px (или дважды выполнить ячейку один раз с магией, а другой раз без магии), но это утомительно, когда я все еще редактирую методы класса в итеративная настройка разработки и оценки.

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

  • В качестве альтернативы я мог бы просто перестать использовать класс и работать только с функциями, которые могут быть отправлены в движки, передав их в качестве первого аргумента в apply_async. Однако мне это тоже не нравится, поскольку я хотел бы прототипировать свой код объектно-ориентированным способом для последующего извлечения из блокнота и включения полученного класса в объектно-ориентированную библиотеку. Сеанс записной книжки служит инструментом совместного прототипирования для обмена идеями между разработчиками, использующими издатель http://nbviewer.ipython.org.

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

Итак, в принципе, есть ли способ определить класс в интерактивном режиме, а затем передать его определение движкам?

Должна быть возможность выбрать определение класса с помощью inspect.getsource в клиенте, затем отправить исходный код в движки и использовать встроенный eval, но, к сожалению, проверка исходного кода не работает для классов, определенных внутри встроенного модуля DummyMod:

TypeError: <IPython.core.interactiveshell.DummyMod object at 0x10c2c4e50> is a built-in class

Есть ли способ вместо этого проверить байт-код определения класса?

Или можно использовать магию %%px, чтобы одновременно выполнять содержимое ячейки локально на клиенте и на каждом движке?


person ogrisel    schedule 06.01.2013    source источник


Ответы (2)


Спасибо за подробный вопрос (и за сообщение мне в Твиттере).

Во-первых, возможно, следует считать ошибкой то, что вы не можете просто запушить классы, потому что простое решение должно быть

rc[:]['MyClass'] = MyClass

но травление интерактивно определенных классов приводит только к ссылке ('\x80\x02c__main__\nMyClass\nq\x01.'), что дает ваш DummyMod AttributeError. Вероятно, это можно исправить внутри сериализации IPython.

Однако перейдем к реальному рабочему решению.

Добавить локальное выполнение к %%px очень просто:

def pxlocal(line, cell):
    ip = get_ipython()
    ip.run_cell_magic("px", line, cell)
    ip.run_cell(cell)
get_ipython().register_magic_function(pxlocal, "cell")

И теперь у вас есть магия %%pxlocal, которая запускает %%px в дополнение к локальному запуску ячейки.

Тогда все, что вам нужно сделать, это:

%%pxlocal

class MyClass(object):
    # etc

чтобы определить свой класс везде. Я добавлю флаг --local к %%px, так что этот дополнительный шаг не нужен.

Полный работающий пример блокнота.

person minrk    schedule 06.01.2013
comment
Спасибо большое Мин! Кажется, трюк с pxlocal работает. Я также нашел способ рассолить определения классов. Через пару минут доведу до сути. - person ogrisel; 07.01.2013
comment
Вот мой эксперимент по сбору определений классов из модуля __main__: gist.github.com/4470212 Хотите мне открыть вопрос для этого парня? - person ogrisel; 07.01.2013
comment
Проблема здесь: ipython/ipython#2754, теперь я попытаюсь быстро создать прототип потенциальное исправление. - person ogrisel; 07.01.2013

Я думаю, вы могли бы использовать "укроп" для выбора интерактивно определенного класса, и вам не нужно беспокоиться о магии %%pxlocal, использовании DummyMod и подделке пространств имен.

Чтобы выбрать класс в интерактивном режиме, просто выполните команду «импортировать укроп», а затем создайте свой класс, как в первый раз. Затем вы сможете отправить его через любую разумную карту или функцию apply_async.

person Mike McKerns    schedule 08.09.2013