Контекст
При разработке интерактивного прототипа на ноутбуке, подключенном к кластеру, я хотел бы определить класс, который доступен как в сеансе клиента __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
, чтобы одновременно выполнять содержимое ячейки локально на клиенте и на каждом движке?