【问题标题】:How to create nested generator structures in python?如何在 python 中创建嵌套的生成器结构?
【发布时间】:2020-11-08 06:30:33
【问题描述】:

我正在尝试创建一个ImageSeries 对象,我想在其中以某种模式检索图像(对于 xy 中的每个值和 z 中的每个值),并且我调用将生成器附加到任务列表,并通过两个 for 循环运行生成器来执行此操作。

但是在第一个任务的第一次迭代之后,我的第二个任务已经筋疲力尽,这不是我想要的结果。我希望第二个任务运行第一个任务的每次迭代。

我想知道是否有有效的方法来编写这样的模式。

class ImageSeries:
    tasks = []

    def xy(self, position):
        print(position)
        yield "xy"

    def z(self, position):
        print(position)
        yield "z"

    def xy_scan(self, positions):
        self.tasks.append((self.xy(pos) for pos in positions))

    def z_scan(self, positions):
        self.tasks.append((self.z(pos) for pos in positions))

    def run(self):
        for i in self.tasks[0]:
            next(i)
            for j in self.tasks[1]:
                next(j)

    def __repr__(self):
        return str(self.tasks)
    

if __name__ == "__main__":
    s = ImageSeries()
    positions = [[0, 0], [100, 100], [1000, 1000]]
    s.xy_scan(positions)
    s.z_scan([0, 100, 1000, 10000])

当前输出:

[0, 0]
0
100
1000
10000
[100, 100]
[1000, 1000]

预期输出:

>>> s.run()
[0, 0]
0
100
1000
10000
[100, 100]
0
100
1000
10000
[1000, 1000]
0
100
1000
10000

【问题讨论】:

  • 当前输出是什么,你想要的输出是什么?

标签: python oop recursion generator object-design


【解决方案1】:

给你

class ImageSeries:
    def __init__(self):
        self._xy_tasks = None
        self._z_tasks = None

    def xy(self, position):
        print(position)
        yield "xy"

    def z(self, position):
        print(position)
        yield "z"

    def xy_scan(self, positions):
        self._xy_tasks = lambda: (self.xy(pos) for pos in positions)

    def z_scan(self, positions):
        self._z_tasks = lambda: (self.z(pos) for pos in positions)

    def run(self):
        for xy_generator in self._xy_tasks():
            next(xy_generator)
            for z_generator in self._z_tasks():
                next(z_generator)

    def __repr__(self):
        return str(self._xy_tasks()) + " " + str(self._z_tasks())


if __name__ == "__main__":
    s = ImageSeries()
    positions = [[0, 0], [100, 100], [1000, 1000]]
    s.xy_scan(positions)
    s.z_scan([0, 100, 1000, 10000])
    s.run()

做了一些事情:

  1. run()
  2. self.tasks 作为一个列表没有意义,因为每个单元格的含义不同,所以我将它分成两个独立的成员变量。
  3. 主要是确保每次运行时都会重新创建生成器,因为它无法重置。我通过使用 lambda 实现了这一点,因此您可以调用一个每次都创建生成器的函数,而不是生成器本身。注意self._xy_tasks()。这会调用一个创建生成器的函数。

【讨论】:

  • 我使用tasks list 来跟踪ImageSeries 中方法被调用的顺序。有时我可能必须在s.xy_scan 之前执行s.z_scan。我还有一个s.exp_scan,我想在将来添加,它不能很好地用于编写循环的方式。但它的工作原理不是这样!
  • 我想我花了一些时间来理解你的答案。它相当聪明,尤其是3。谢谢:)
  • 有没有办法可以使用itertools.product() 之类的东西来概括超过 2 个嵌套 for 循环的迭代?
  • @pskeshu 可能,但我会问一个新问题。
【解决方案2】:

生成器不知道它们是嵌套的。第一次发电机用完后,就结束了。实际上,在这种情况下您不需要生成器,因为它们在您迭代不想存储在内存中的长列表时有意义。但是在这里你必须将所有重复的序列存储在内存中。您只能在上层循环中使用生成器。但只有当它真的很长并且从某个流中接收时它才有意义。如果它已经在内存中,则您实际上并不需要生成器。您想要的一切都可以更简单地完成

xy_list = [[0, 0], [100, 100], [1000, 1000]]
z_list = [0, 100, 1000, 10000]
for xy in xy_list:
    print(xy)
    for z in z_list:
        print(z)

如果你需要它成为一个类,只需使用xy_scanz_scan 保存到self.xy_listself.z_list 并在run 方法中使用相同的for 循环(只需添加self.xy_listz_list)

【讨论】:

  • 我使用生成器在 yield 语句之间交错代码,所以这种模式最适合我。
  • 你可以把你想要的代码放在print附近的for循环中。
猜你喜欢
  • 2018-05-18
  • 1970-01-01
  • 1970-01-01
  • 2018-12-18
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 2018-04-14
  • 2011-06-26
相关资源
最近更新 更多