为什么要在循环时退出StopIteration

时间:2019-06-22 19:10:37

标签: python python-3.x generator

我正在使用yield from,但是我不知道whileyield的影响。如果我将yield from放入while循环中,则效果很好,但是同时取消循环时会发生异常。

final_result = {}
def sales_sum(pro_name):
    total = 0
    nums = []
    while True:
        x = yield
        print(pro_name+" Sales volume: ", x)
        if not x:
            break
        total += x
        nums.append(x)
    return total, nums

def middle(key):
    while True:
        final_result[key] = yield from sales_sum(key)

def middle2(key):
    final_result[key] = yield from sales_sum(key)

def main(fun):
    data_sets = { "A": [1200, 1500], "B": [28,55,98]}
    for key, data_set in data_sets.items():
        m = fun(key)
        m.send(None)
        for value in data_set:
            m.send(value)
        m.send(None)

if __name__ == '__main__':
    main(middle) # work well
    main(middle2) # StopIteration

我希望main(middle2)main(middle)一样好,但是有一个StopIteration例外。

2 个答案:

答案 0 :(得分:1)

program server; {$mode delphi}{$H+} uses {$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF} Classes , Windows , Winsock , SysUtils { you can add units after this }; procedure SendFullnameTest(); var MyData:WSADATA; result:Integer; s:TSocket; SendBuf:Array[0..31] of AnsiChar; clientservice:sockaddr_in; BytesSent:Integer; begin try result:= WSAStartup(MAKEWORD (2,2), MyData); if result = NO_ERROR then begin s := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if s <> INVALID_SOCKET then begin clientservice.sin_family := AF_INET; clientservice.sin_addr.s_addr := inet_addr('127.0.0.1'); clientservice.sin_port := htons(8080); if connect(s,clientservice,sizeof(clientservice)) <> SOCKET_ERROR then begin sendbuf := 'I am a Sent message'; bytesSent := send(s,sendbuf,Length(sendbuf),0); writeln('Bytes send: ',bytesSent); end else writeln('Failed to connect');; end else writeln('Error at Socket: ',WSAGetLastError);; end else writeln('Error at WSAStartup'); finally Writeln(SysErrorMessage(GetLastError)); WSACleanUp; Readln; end; end; begin SendFullnameTest(); ReadLn; end. 是一个有限迭代器。 sales_sum仅在其上迭代一次; middle2尝试对其进行多次迭代。

答案 1 :(得分:1)

StopIteration中意外的main异常的原因是您的m.send(None)调用导致middle2生成器用尽(在子生成器{{1}之后}响应收到的falsey值而跳出循环。发电机耗尽时,它会升高sales_sum。通常,这是不可见的,因为您在StopIteration循环中使用了迭代器,但是在这种情况下,它将破坏您的代码。

有几种方法可以解决此问题。一种是对for使用两个参数的调用,而不是使用next

m.send(None)

这与next(m, None) 的作用相同,但是具有抑制m.send(None)的附加好处。请注意,对StopIteration的调用中的Nonenext中的调用并不完全相同。对于迭代器用尽的情况,它是默认的返回值,而不是发送的值(使用send时始终为None)。

另一种方法是更改​​next,以使其在middle2生成器执行时不会结束。您可以在末尾添加一个额外的sales_sum语句,以便在其子生成器返回时对yield进行赋值后,它最后一次产生控制权。

最后的想法是将final_result替换为m.send(None)。这将需要对m.close()进行一些更改,因为final_result调用将向生成器中抛出close异常。如果您希望的话,可以将其用作完成操作的信号,而不用寻找虚假的值:

GeneratorExit

有了此更改,def sales_sum(pro_name): total = 0 nums = [] while True: try: x = yield except GeneratorExit: return total, nums print(pro_name+" Sales volume: ", x) total += x nums.append(x) 无需进行任何修改。