8

语境

在连接到集群的笔记本上的交互式原型开发中,我想定义一个在客户端__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函数中,但我发现这不切实际,因为我想稍后在我的笔记本中的其他函数中重用该类定义。

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

  • 最后的替代方法是在文件系统上的文件上的 python 模块中编写我的类,然后使用 NFS 将该文件发送到引擎 PYTHONPATH。这有效,但阻止我只在笔记本环境中工作,这违背了笔记本中交互式原型设计的全部目的。

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

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

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

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

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

4

2 回答 2

9

感谢您提出详细的问题(并在 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,因此不需要这个额外的步骤。

一个完整的工作示例笔记本

于 2013-01-06T20:52:25.557 回答
2

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

要交互式地腌制一个类,只需执行“import dill”,然后像第一次那样构建你的类。然后,您应该能够通过任何健全的映射或 apply_async 函数发送它。

于 2013-09-08T23:41:10.063 回答