在连接到集群的笔记本上进行交互式原型开发时,我想定义一个类,该类既可以在客户端 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 模块编写我的类,并使用 NFS 将该文件发送到引擎的 PYTHONPATH。这是有效的,但阻止我只在笔记本环境中工作,这违背了笔记本中交互式原型的全部目的。
因此,基本上,有没有办法交互式地定义一个类,然后将其定义发送给引擎?
可以使用客户端的 inspect.getsource 序列化类定义,然后将源代码发送到引擎并使用 eval 内置函数,但不幸的是,源代码检查不适用于 DummyMod 内置模块中定义的类:
TypeError: <IPython.core.interactiveshell.DummyMod object at 0x10c2c4e50> is a built-in class
有没有办法检查类定义的字节码?
或者是否可以使用 %%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
在所有地方定义您的类。
我将在 %%px 中添加 --local 标志,因此此额外步骤不是必需的。
完整的、可运行的示例笔记本。
430

被折叠的 条评论
为什么被折叠?



