【问题标题】:Replacing C++ STL output iterator with a Python generator用 Python 生成器替换 C++ STL 输出迭代器
【发布时间】:2013-04-25 07:39:15
【问题描述】:

Python 没有内置的 OutputIterator 等价物;特别是,内置或标准库容器不支持任何允许客户端代码在不知道特定容器类型的情况下向它们发送数据的通用接口。

根据@Steven Rumbalski's comment@Glenn Maynard's answer,这通常不是问题,因为在 C++ 中将采用 OutputIterator 参数的函数在 python 中将被简单地编写为生成器。

通常,我使用生成器没有问题,并且从不觉得我需要 Python 中的 OutputIterator。但是,在这种情况下,我被卡住了。

我正在用 Python 重新实现 Boost Graph Library 中的一些算法。一个典型的图遍历算法,比如depth_first_search,将“访问者”对象作为参数。访问者本质上是遍历算法在执行过程中遇到不同事件时调用的一组回调函数(例如,发现新顶点、检查边等)。在 C++ 中,我可以让这些回调函数中的一个或多个将数据发送到访问者对象在其初始化时从客户端代码获得的 OutputIterator 对象。 (例如,这正是topological_sort 的实现方式:它接受一个OutputIterator,将其传递给dfs_visitor 对象,然后访问者对象“监控”事件finished_vertex 并将它接收到的顶点发送到指定的OutputIterator。当然,更复杂的情况需要多个 OutputIterator 对象和多个回调函数。)

如何使用 Python 生成器实现相同的目标?

我需要以某种方式以生成器“样式”从depth_first_search 向多个指定的数据消费者发送数据。我只是不知道该怎么做。 (我使用的是 Python 3.3。)

【问题讨论】:

    标签: c++ python generics boost python-3.x


    【解决方案1】:

    你可以只传递回调函数吗?

    def depth_first_search(some_args, on_edge=lambda e:None, on_vertex=lambda v:None):
        ...
        on_edge(some_edge)
        on_vertex(some_vertex)
    
    def edge_handler(e):
        print "E", e
    
    def vertex_handler(v):
        print "V", v
    
    depth_first_search(..., on_edge=edge_handler, on_vertex=vertex_handler)
    

    或者产生一个目的地:

    def depth_first_search(some_args, on_edge=lambda e:None, on_vertex=lambda v:None):
        ...
        yield "edge", some_edge
        yield "vertex", some_vertex
    
    for t, value in depth_first_search(...):
        if t == 'edge':
            # ...
        elif t == 'vertex':
            # ...
    

    【讨论】:

    • 如果你只想打印每个顶点,你可以。然而,在现实场景中,您需要将vertex_handler 接收到的顶点发送到数据处理管道的下方(例如,使用其他生成器函数对其进行处理)。因此,edge_handler 本身需要是一个生成器;但我不知道如何,因为它的调用者使用它将数据推送到其中,而不是从中接收数据。
    • @Max:我不确定我是否理解这里的目标。你能写一些你希望能够转换成 python 的最小 C++ 代码吗?
    • @max:我在猜测你的最终目标是什么。在某些情况下,短信会很好。这取决于边和顶点处理的独立程度
    • 是的,你是对的,这是一种合理的方法。不过,我一直在寻找稍微不同的东西;我没有解释清楚。我将很快展示一个示例。
    【解决方案2】:

    generator.send 方法在这里做你想要的,我认为:

    def depth_first_search(some_args, edge_consumer, vertex_consumer):
        # start the generators
        next(edge_consumer)
        next(vertex_consumer)
    
        ...
        edge_consumer.send(some_edge)
        vertex_consumer.send(some_vertex)
    
        ...
        # this raises GeneratorExit at the `yield` in the generator
        edge_consumer.close()
        vertex_consumer.close()
    
    def edge_handler():
        while True:
            e = yield
            print "E", e
    
    def vertex_handler():
        while True:
            v = yield
            print "V", v
    
    depth_first_search(..., edge_handler(), vertex_handler())
    

    【讨论】:

      【解决方案3】:

      我不知道这是否可行,但是使用像 multimethods 这样的库来实现多调度函数来完成您需要的工作怎么样?我的 Internet 出现 DNS 问题,因此我无法查找语法,因此您将获得伪代码而不是真正的 Python,但以下是我所说的大致概念:

      def send_to(data, consumer):
          workfunc = dispatch(type(consumer))
          workfunc(data, consumer)
      
      def send_to_list(data, consumer):
          consumer.append(data)
      
      def send_to_set(data, consumer):
          consumer.add(data)
      
      def send_to_file_obj(data, consumer):
          consumer.write(data)
      

      当然,需要一些管道来将工作函数连接到调度函数,而这正是我现在实际上无法查找的内容,因为我的 DNS 已关闭。 (幸运的是,StackOverflow 仍在我的浏览器缓存中)。所以我担心这个答案是笼统的,而具体的,但希望它至少会为你指明一个有用的方向。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-01
        • 2012-01-21
        • 2014-08-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多