如何在IPython.parallel中使用交互式定义的类?

时间:2013-01-06 17:15:03

标签: python parallel-processing ipython ipython-notebook

上下文

在连接到群集的笔记本上的交互式原型开发中,我想定义一个在客户端__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属性)然后只为剩余的参数做一个简单的pickle。 / p>

可能的解决方案(对我不起作用)

  • 另一种解决方案是在持有类%%px定义的单元格上使用MyClass单元格魔术。但是,这将阻止我在客户端脚本中构建也执行调度的类实例。我需要在不使用%%px魔法的情况下将单元格内容复制并粘贴到其他单元格中(或者使用魔法执行两次单元格,而不使用魔法执行另一次单元格)但是当我仍在编辑方法时,这将是乏味的。迭代开发中的类&amp;评估设置。

  • 另一种解决方案是将类定义嵌入到process函数中,但我觉得这不实用,因为我想在我的笔记本中稍后在其他函数中重用该类定义。

  • 或者我可以停止使用一个类,只使用可以传递给引擎的函数,然后将第一个参数传递给apply_async。但是我不喜欢这样,因为我想以面向对象的方式对我的代码进行原型设计,以便以后从笔记本中提取并将结果类包含在面向对象的库中。笔记本会话用作协作原型工具,用于使用http://nbviewer.ipython.org发布商在开发人员之间交换想法。

  • 最后的选择是在python模块中将我的类写在文件系统上的文件中,然后使用NFS将该文件发送到引擎PYTHONPATH。这有效但阻止我只在笔记本电脑环境中工作,这会破坏笔记本电脑中交互式原型制作的全部目的。

基本上,有没有办法以交互方式定义类,然后将其定义发送到引擎?

应该可以使用客户端中的inspect.getsource来挑选类定义,然后将源发送到引擎并使用内置eval但不幸的是,源检查不适用于内部定义的类DummyMod内置模块:

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

有没有办法检查类定义的字节码呢?

或者是否可以使用%%px魔法,以便在客户端和每个引擎上本地执行单元格的内容?

2 个答案:

答案 0 :(得分:7)

感谢您提出详细的问题(并在Twitter上发帖告诉我)。

首先,也许它应该被认为是一个你不能只推动课程的错误, 因为简单的解决方案应该是

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标记,因此不需要执行此额外步骤。

完整,有效example notebook

答案 1 :(得分:2)

我认为你可以使用“dill”来挑选交互式定义的类,而不必担心%% pxlocal魔术,使用DummyMod和伪造命名空间。

要以交互方式挑选课程,只需执行“导入莳萝”,然后像第一次一样构建课程。您应该能够通过任何理智的地图或apply_async函数发送它。