你称之为具有两种不同"完成"状态的迭代器是什么?

Chi*_*aca 6 python naming-conventions

在查询具有未知长度的分页列表的API时,我发现自己基本上在做

def fetch_one(self, n):
    data = json.load(urlopen(url_template % n))
    if data is None:
        self.finished = True
        return
    for row in data:
        if row_is_weird(row):
            self.finished = True
            return
        yield prepare(row)

def work(self):
    n = 1
    self.finished = False
    while not self.finished:
        consume(self.fetch_one(n))
        n += 1
Run Code Online (Sandbox Code Playgroud)

之间的分裂work,并fetch_one使得它很容易测试,但通过实例变量的信令意味着我不能有一个以上的work在同一时间,这吮吸回事.我提出了我认为更清洁的解决方案,但它涉及一个具有两个"完成"状态的迭代器,我不知道该怎么称呼它.我确定这种模式存在于其他地方,所以我很欣赏指针(或者为什么这是愚蠢的原因):

class Thing(object):
    def __init__(self, gen):
        self.gen = gen
        self.finished = False

    def __iter__(self):
        return self

    def __next__(self):
        try:
            v = next(self.gen)
        except StopThisThing:
            self.finished = True
            raise StopIteration
        else:
            return v
    next = __next__
Run Code Online (Sandbox Code Playgroud)

然后我会用它

@thinged
def fetch_one(self, n):
    data = json.load(urlopen(url_template % n))
    if data is None:
        raise StopThisThing()
    for row in data:
        if row_is_weird(row):
            raise StopThisThing()
        yield prepare(row)

def work(self):
    n = 1
    while True:
        one = self.fetch_one(n)
        consume(one)
        if one.finished:
            break
        n += 1
Run Code Online (Sandbox Code Playgroud)

那么我创造的这件事是什么?

Rik*_*ggi 2

我认为你可以通过产生一些特别的东西来避免这种情况。

我必须构建自己的可运行示例,以展示我的意思:

def fetch_one(n):
    lst = [[1,2,3], [4,5,6], [7,8,9]][n]
    for x in lst:
        if x == 6:
            yield 'StopAll'
            return
        yield x

def work():
    n = 0
    in_progress = True
    while in_progress:
        numbers_iterator = fetch_one(n)
        for x in numbers_iterator:
            if x == 'StopAll':
                in_progress = False
                break
            print('x =', x)
        n += 1

work()
Run Code Online (Sandbox Code Playgroud)

输出:

x = 1
x = 2
x = 3
x = 4
x = 5
Run Code Online (Sandbox Code Playgroud)

与你构建的装饰器相比,我更喜欢这个self.finished,但我认为仍然可以找到更好的东西。(也许这个答案可以帮助你)。

更新:一个更简单的解决方案可能是转换fetch_one为带有自己标志的类finised

此解决方案的装饰器方法可能是:

class stopper(object):
    def __init__(self, func):
        self.func = func
        self.finished = False

    def __call__(self, *args, **kwargs):
        for x in self.func(*args, **kwargs):
            if x == 6:
                self.finished = True
                raise StopIteration
            yield x
        else:
            self.finished = True
Run Code Online (Sandbox Code Playgroud)

基本上你不再关心它是如何fetch_one工作的,只关心产量是否可以。

使用示例:

@stopper
def fetch_one(n):
    lst = [[1,2,3], [4,5,6], [7,8,9]][n]
    #lst = [[1,2,3], [], [4,5,6], [7,8,9]][n]   # uncomment to test for/else
    for x in lst:
        yield x

def work():
    n = 0
    while not fetch_one.finished:
        for x in fetch_one(n):
            print('x =', x)
        n += 1
Run Code Online (Sandbox Code Playgroud)