Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/350.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 有两个不同的“值”的迭代器叫什么;“完成”;州?_Python_Naming Conventions - Fatal编程技术网

Python 有两个不同的“值”的迭代器叫什么;“完成”;州?

Python 有两个不同的“值”的迭代器叫什么;“完成”;州?,python,naming-conventions,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 =

在查询具有未知长度的分页列表的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
work
fetch_one
之间的分离使得测试非常容易,但是通过实例变量发送的信号意味着我不能同时进行多个
工作,这很糟糕。我提出了一个我认为更干净的解决方案,但它涉及一个具有两个“完成”状态的迭代器,我不知道该如何称呼它。我确信这种模式在其他地方也存在,所以我希望能得到一些提示(或者这很愚蠢的原因):

然后我会像这样使用它

@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

那么我创造的东西是什么呢?

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

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

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()
输出:

x = 1
x = 2
x = 3
x = 4
x = 5
比起self.finished或者像你建造的那样的装饰师,我更喜欢这个,但是我认为仍然可以找到更好的东西。(也许这个答案可以帮你解决这个问题)

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

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

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
基本上,你不再关心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

你发明的名字是“穷人版迭代器”。您的
work
函数正在花费精力重新实现python在for循环中已经提供的功能。您已经得到了一系列可以随时停止的值,这正是python迭代器提供这些值的原因。我们最好将一些逻辑移到一个单独的函数中。大概是这样的:

def fetch_all(self):
    for n in itertools.count():
        data = json.load(urlopen(url_template % n))
        if data is None:
            return

        for row in data:
             if row_is_wierd(row):
                  return

        yield itertools.imap(prepare, data)
from itertools import imap, count
jsonsource = imap(lambda n: json.load(urlopen(url_template % n)), count(1))
for page in pagegetter(linegetter(jsonsource, row_is_weird)):
    consume(page)
或者,您可以使用异常

def fetch_all(self):
    for n in itertools.count():
        data = json.load(urlopen(url_template % n)
        if data is None:
             return

        try:
             yield map(prepare, data)
        except WierdRowError:
             return
事实上,我质疑以这种方式处理wierd行背后的逻辑。是什么让争吵变得更激烈?我们为什么停在那里?行是wierd真的是某种错误吗

在任何情况下,你的工作职能

def work():
    for item in fetch_all():
        consume(item)
编辑

如果有更多的信息,我会这样做

def fetch_rows():
    for n in itertools.count():
        data = json.load(urlopen(url_template % n))
        for row in data:

            if row_is_wierd(row):
                return
            yield row
此函数用于生成行序列

def work():
    for row in fetch_all_rows():
         consume(row)
此函数实际上处理行


可以用itertools中的迭代器对象替换其中的部分或全部对象。

有一种更干净的方法来处理您的情况:您有一个由分页数据组成的数据源,但是可以通过检查单个行来检测终止条件。因此,我将使用迭代器逐行获取数据,并在应该时停止。无特殊值(带内或带外),无双向通信

编辑:我刚刚发现,事实上,您并不关心页面边界。在这种情况下,您只需使用:

def linegetter(url_template):
    """
    Return the data line by line. Stop when end of input is detected.
    """
    n=0
    while True:
        n += 1
        data = json.load(urlopen(url_template % n))
        if data is None:
            return
        for row in data:
            if row_is_weird(row):
                return
            yield row
它逐行返回数据,您可以按任何方式准备和使用数据。完成了

这似乎是全部答案。但是假设您需要按页处理数据(正如您的代码现在所做的那样)。只需将第一个迭代器的输出分组为每个页面的子迭代器。代码更复杂,因为我粘贴了一个完全通用的解决方案;但是使用它真的很简单

def linegetter(source, terminate=lambda x: False):
    """
    Return the data line by line, in a tuple with the page number.
    Stop when end of input is detected.
    """
    for n, data in enumerate(source):
        if data is None:
            return
        for row in data:
            if terminate(row):
                return
            yield (n, row)

def _giverow(source):
    "Yield page contents line by line, discarding page number"
    for page, row in source:
        yield row

def pagegetter(source):
    """Return an iterator for each page of incoming data.
    """
    import itertools
    for it in itertools.groupby(source, lambda x : x[0]):
        yield _giverow(it[1])
演示:每一行是一个数字,每一页是一个子列表。当我们看到“b”时,我们停下来。您的主循环现在没有终止检查:

incoming = iter([[1,2,3], [4,5,6, "b", 7], [7,8,9]])
def row_is_weird(r): 
    return r == "b"

for page in pagegetter(linegetter(incoming, row_is_weird)):
    print list(page)
如您所见,代码是完全通用的。您可以将其与获取json页面的迭代器一起使用,如下所示:

def fetch_all(self):
    for n in itertools.count():
        data = json.load(urlopen(url_template % n))
        if data is None:
            return

        for row in data:
             if row_is_wierd(row):
                  return

        yield itertools.imap(prepare, data)
from itertools import imap, count
jsonsource = imap(lambda n: json.load(urlopen(url_template % n)), count(1))
for page in pagegetter(linegetter(jsonsource, row_is_weird)):
    consume(page)

我最初的回答是错误的;这里有一个更好的

您有几个序列(JSON文件),可以正常结束,也可以突然结束(如果需要)
行很奇怪
)。如果序列正常结束,则必须执行下一个序列。当您得到的是
None
而不是JSON文件时,此序列结束。

您可以使用实例变量来表示突然结束和正常结束。这有助于代码打破深度嵌套的循环,但也会引入不需要的非局部状态

删除共享状态最简单的方法是将其作为结果或参数的一部分传递。让我们把每一行的“古怪”和它一起传递。事实上,如果一行很奇怪,我们不需要传递行值,我们只需要传递一个值,说“从现在开始,结果无效”。它有助于在正确的位置停止迭代

从本质上讲,它看起来像是被接受的答案,但在内部,您可以将其视为。附加的好处是,您永远不会将序列结束标记误认为序列标记

# preparations and mockups

input = [ # imitates rows or parsed JSON
  ['apple', 'orange', 'peach'], # entirely good rows
  ['meat', 'fowl', 'ROTTEN', 'unicorn'], # some good rows, then a bad one
  ['unicorn2', 'unicorn3'], # good rows we should never see
  None, # sentinel imitating 'no data' from JSON parser
]

def prepare(x): 
  print "%s is prepared" % x
  return 'prepared %s' %x

consume = lambda x: "%s is consumed" % x

row_is_weird = lambda x: x is 'ROTTEN'

# the solution

def maybe_prepare(row):
  if row_is_weird(row):
    return (False, None) # Nothing
  else:
    return (True, prepare(row)) # Just prepare(row)

def fetch_one(n):
  data = input[n-1] # instead of json.load(template % n)
  if data is None:
    return iter([(False, None)])
  else:
    return (maybe_prepare(row) for row in data)

# chain_all iterates over all items of all sequences in seqs 
chain_all = lambda seqs: (item for seq in seqs for item in seq)

from itertools import count
def work():
  for is_ok, prepared_row in chain_all(fetch_one(n) for n in count(1)):
    if not is_ok:
      break
    print consume(prepared_row)
这段代码仍然很容易测试,但是测试
fetch_one()
要稍微复杂一些:只需在第一个
(False,None)
之前迭代值。这可以通过
itertools.takewhile()
轻松完成


函数
maybe\u prepare()
可以是一行,但为了可读性,我将其保留为多行。

不会尝试/除了
one=self.fetch\u one(n)
达到同样的结果?@RikPoggi我不这么认为:在fetch_one已经产生有用的数据之后,可能会发生终止。请编辑以使示例更清楚WRT@RikPoggi的观点。。那是完全不同的情况。但是我仍然不喜欢decorator和decorred函数的耦合(一个不能脱离另一个),在您的代码中,您似乎并不真正关心数据为何完成。那么为什么不为任何终止条件简单地提出StopIteration呢?这当然会起作用,但感觉就像我做了我讨厌这个API的同样事情:带内没有更多的东西信息。另一方面,我是这个特殊产品的唯一消费者,所以…@Chipaca:是的,你在某种程度上受你试图使用的设计的约束,在它之上构建将是最简单的解决方案。您想要什么样的API?不同的设计可以使用线程,但我不知道最终是否值得;这个